From 2143aa4e799d0febb6521dfc603a681d44be15e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 15:50:17 +0000 Subject: [PATCH 1/5] Initial plan From bd157bec60f1b48f37e657f3b2d4dc65f974e77c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:04:12 +0000 Subject: [PATCH 2/5] Align HTML report filename path semantics Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../HtmlReportEngine.cs | 58 ++++++++- .../HtmlReportGeneratorCommandLine.cs | 114 ++++++------------ ...osoft.Testing.Extensions.HtmlReport.csproj | 1 + .../Resources/ExtensionResources.resx | 6 +- .../Resources/xlf/ExtensionResources.cs.xlf | 10 +- .../Resources/xlf/ExtensionResources.de.xlf | 10 +- .../Resources/xlf/ExtensionResources.es.xlf | 10 +- .../Resources/xlf/ExtensionResources.fr.xlf | 10 +- .../Resources/xlf/ExtensionResources.it.xlf | 10 +- .../Resources/xlf/ExtensionResources.ja.xlf | 10 +- .../Resources/xlf/ExtensionResources.ko.xlf | 10 +- .../Resources/xlf/ExtensionResources.pl.xlf | 10 +- .../xlf/ExtensionResources.pt-BR.xlf | 10 +- .../Resources/xlf/ExtensionResources.ru.xlf | 10 +- .../Resources/xlf/ExtensionResources.tr.xlf | 10 +- .../xlf/ExtensionResources.zh-Hans.xlf | 10 +- .../xlf/ExtensionResources.zh-Hant.xlf | 10 +- .../HelpInfoAllExtensionsTests.cs | 4 +- .../HtmlReportTests.cs | 22 +++- .../HtmlReportEngineTests.cs | 88 ++++++++++++++ .../HtmlReportGeneratorCommandLineTests.cs | 87 ++++++------- 21 files changed, 323 insertions(+), 187 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs index a16e0dad4f..21c44a812b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Testing.Extensions.HtmlReport.Resources; +using Microsoft.Testing.Platform; using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions.TestFramework; @@ -64,11 +65,16 @@ public HtmlReportEngine( out string[]? providedFileName); string fileName = fileNameExplicitlyProvided - ? providedFileName![0] + ? ResolveHtmlFileName(providedFileName![0]) : BuildDefaultFileName(finishTime); string outputDirectory = _configuration.GetTestResultDirectory(); string finalPath = Path.Combine(outputDirectory, fileName); + string? finalDirectory = Path.GetDirectoryName(finalPath); + if (!RoslynString.IsNullOrEmpty(finalDirectory)) + { + _fileSystem.CreateDirectory(finalDirectory); + } string template = LoadTemplate(); string json = BuildJson(results, finishTime); @@ -154,6 +160,19 @@ private string BuildDefaultFileName(DateTimeOffset finishTime) return ReplaceInvalidFileNameChars(raw); } + private string ResolveHtmlFileName(string template) + { + string processName = Path.GetFileNameWithoutExtension(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath()); + string processId = _environment.ProcessId.ToString(CultureInfo.InvariantCulture); + Dictionary replacements = ArtifactNamingHelper.GetStandardReplacements(processName, processId, _clock.UtcNow); + string resolved = ArtifactNamingHelper.ResolveTemplate(template, replacements); + string directoryPart = Path.GetDirectoryName(resolved) ?? string.Empty; + string sanitizedFileName = ReplaceInvalidFileNameChars(Path.GetFileName(resolved)); + return directoryPart.Length == 0 + ? sanitizedFileName + : Path.Combine(directoryPart, sanitizedFileName); + } + private static string GetTargetFrameworkMoniker() => TargetFrameworkParser.GetShortTargetFramework( Assembly.GetEntryAssembly()?.GetCustomAttribute()?.FrameworkDisplayName) @@ -163,13 +182,44 @@ private static string GetTargetFrameworkMoniker() private static string ReplaceInvalidFileNameChars(string fileName) { var sb = new StringBuilder(fileName.Length); - char[] invalid = Path.GetInvalidFileNameChars(); foreach (char c in fileName) { - sb.Append(Array.IndexOf(invalid, c) >= 0 ? '_' : c); + sb.Append(IsInvalidFileNameChar(c) ? '_' : c); } - return sb.ToString(); + string replaced = sb.ToString().TrimEnd(); + if (IsReservedFileName(replaced)) + { + replaced = '_' + replaced; + } + + return replaced; + } + + private static bool IsInvalidFileNameChar(char c) + => c is < ' ' or '"' or '<' or '>' or '|' or ':' or '*' or '?' or '\\' or '/' or '@' or '(' or ')' or '^' or ' '; + + private static bool IsReservedFileName(string fileName) + { + string bareName = fileName; + int dot = bareName.IndexOf('.'); + if (dot >= 0) + { + bareName = bareName.Substring(0, dot); + } + + return bareName.Equals("CON", StringComparison.OrdinalIgnoreCase) + || bareName.Equals("PRN", StringComparison.OrdinalIgnoreCase) + || bareName.Equals("AUX", StringComparison.OrdinalIgnoreCase) + || bareName.Equals("NUL", StringComparison.OrdinalIgnoreCase) + || bareName.Equals("CLOCK$", StringComparison.OrdinalIgnoreCase) + || IsReservedNameWithNumber(bareName, "COM") + || IsReservedNameWithNumber(bareName, "LPT"); + + static bool IsReservedNameWithNumber(string bareName, string prefix) + => bareName.Length == 4 + && bareName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) + && bareName[3] is >= '1' and <= '9'; } private static string LoadTemplate() diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs index 5c798c1101..18d9c5dae0 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs @@ -14,6 +14,8 @@ internal sealed class HtmlReportGeneratorCommandLine : ICommandLineOptionsProvid public const string HtmlReportOptionName = "report-html"; public const string HtmlReportFileNameOptionName = "report-html-filename"; + private static readonly char[] DirectorySeparators = [Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar]; + /// public string Uid => nameof(HtmlReportGeneratorCommandLine); @@ -40,19 +42,28 @@ public Task ValidateOptionArgumentsAsync(CommandLineOption com { if (commandOption.Name == HtmlReportFileNameOptionName) { - string fileName = arguments[0]; + if (arguments.Length is 0) + { + return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameShouldNotContainPath); + } - // Validate "pure file name" first. We don't want any path component, drive letter, - // parent directory traversal, leading/trailing whitespace or invalid file name char. - if (!IsValidPureFileName(fileName)) + string argument = arguments[0]; + + string fileNamePart = Path.GetFileName(argument); + if (RoslynString.IsNullOrWhiteSpace(fileNamePart)) { return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameShouldNotContainPath); } - if (!fileName.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) + if (!fileNamePart.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) { return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameExtensionIsNotHtml); } + + if (EscapesResultsDirectory(argument)) + { + return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameShouldNotContainPath); + } } return ValidationResult.ValidTask; @@ -65,75 +76,30 @@ public Task ValidateCommandLineOptionsAsync(ICommandLineOption ? ValidationResult.InvalidTask(ExtensionResources.HtmlReportIsNotValidForDiscovery) : ValidationResult.ValidTask; - // We are intentionally strict here so that we cannot be tricked across platforms. - // The argument must be a "pure" file name: no directory separator, no drive letter, - // no parent directory traversal, no invalid file name character, no leading/trailing - // whitespace, no Windows reserved device name. We use a hard-coded list of invalid - // characters (a superset of Path.GetInvalidFileNameChars() on Linux + Windows) so - // the same input is rejected regardless of the host OS. - private static readonly char[] InvalidFileNameChars = - [ - '\0', '/', '\\', ':', '*', '?', '"', '<', '>', '|', - '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007', - '\b', '\t', '\n', '\u000b', '\u000c', '\r', - '\u000e', '\u000f', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', - '\u0015', '\u0016', '\u0017', '\u0018', '\u0019', '\u001a', '\u001b', - '\u001c', '\u001d', '\u001e', '\u001f', - ]; - - // Windows reserved device names. CreateFile on Windows will redirect a file - // named e.g. CON.html to the actual device. Rejecting them up-front means the - // option doesn't pass validation but then explode later in WriteAsync. - private static readonly string[] WindowsReservedNames = - [ - "CON", "PRN", "AUX", "NUL", - "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", - ]; - - private static bool IsValidPureFileName(string fileName) - { - if (RoslynString.IsNullOrWhiteSpace(fileName)) - { - return false; - } - - if (fileName != fileName.Trim()) - { - return false; - } - - if (fileName == "." || fileName == ".." || fileName.Contains("..")) - { - return false; - } - - foreach (char c in fileName) - { - if (Array.IndexOf(InvalidFileNameChars, c) >= 0) - { - return false; - } - } - - // Disallow Windows device names independent of host OS so the option is - // consistently rejected. We compare against the bare name (without extension) - // because e.g. "CON.html" maps to the CON device. - string bareName = fileName; - int dot = bareName.IndexOf('.'); - if (dot >= 0) - { - bareName = bareName.Substring(0, dot); - } - - foreach (string reserved in WindowsReservedNames) - { - if (string.Equals(bareName, reserved, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } + private static bool EscapesResultsDirectory(string path) + => !IsPathFullyQualified(path) + && (Path.IsPathRooted(path) + || path.Split(DirectorySeparators, StringSplitOptions.RemoveEmptyEntries).Any(segment => segment == "..")); - return true; + private static bool IsPathFullyQualified(string path) + { +#if NETCOREAPP + return Path.IsPathFullyQualified(path); +#else + return path.Length >= 2 + && ((IsDirectorySeparator(path[0]) && IsDirectorySeparator(path[1])) + || (Path.DirectorySeparatorChar == '/' + ? path[0] == '/' + : path.Length >= 3 + && IsValidDriveLetter(path[0]) + && path[1] == ':' + && IsDirectorySeparator(path[2]))); + + static bool IsDirectorySeparator(char c) + => c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; + + static bool IsValidDriveLetter(char c) + => c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z'); +#endif } } diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj index 83a09753a0..f3d2d5f0da 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Microsoft.Testing.Extensions.HtmlReport.csproj @@ -44,6 +44,7 @@ This package extends Microsoft Testing Platform to produce self-contained HTML t + diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx index 3e5d997cf1..69c572d778 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx @@ -71,13 +71,15 @@ '--report-html-filename' file name argument must end with '.html' (e.g. --report-html-filename myreport.html) - The name of the generated HTML report + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html '--report-html-filename' requires '--report-html' to be enabled - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) Produce a self-contained HTML report for the current test session diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf index db2ff9b5fc..bbbb2fce7a 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Název vygenerované sestavy HTML + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Název vygenerované sestavy HTML @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - Argument názvu souboru nesmí obsahovat cestu nebo neplatné znaky (např. --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + Argument názvu souboru nesmí obsahovat cestu nebo neplatné znaky (např. --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf index fe2c7404f8..7f1f442d65 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Der Name des generierten HTML-Berichts. + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Der Name des generierten HTML-Berichts. @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - Das Dateinamenargument darf keinen Pfad und keine ungültigen Zeichen enthalten (z. B. --report-html-filename myreport.html). + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + Das Dateinamenargument darf keinen Pfad und keine ungültigen Zeichen enthalten (z. B. --report-html-filename myreport.html). diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf index 75b6592d33..23741e9f4b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Nombre del informe HTML generado + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Nombre del informe HTML generado @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - el argumento de nombre de archivo no debe contener una ruta de acceso ni caracteres no válidos (por ejemplo, --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + el argumento de nombre de archivo no debe contener una ruta de acceso ni caracteres no válidos (por ejemplo, --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf index 3b28815b6e..9c140b4ada 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Nom du rapport HTML généré + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Nom du rapport HTML généré @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - L’argument de nom de fichier ne doit pas contenir de chemin d’accès ni de caractères non valides (par exemple, --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + L’argument de nom de fichier ne doit pas contenir de chemin d’accès ni de caractères non valides (par exemple, --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf index 8d2ec09e31..631b4751ec 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Nome del report HTML generato + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Nome del report HTML generato @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - l'argomento del nome file non deve contenere un percorso o caratteri non validi (ad esempio --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + l'argomento del nome file non deve contenere un percorso o caratteri non validi (ad esempio --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf index 97338ab96a..2ba7456b17 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - 生成された HTML レポートの名前 + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + 生成された HTML レポートの名前 @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - ファイル名引数にパスや無効な文字を含めることはできません (例: --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + ファイル名引数にパスや無効な文字を含めることはできません (例: --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf index 38ad8285c6..75271a1b96 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - 생성된 HTML 보고서의 이름 + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + 생성된 HTML 보고서의 이름 @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - 파일 이름 인수에는 경로나 잘못된 문자가 포함되면 안 됩니다. 예: --report-html-filename myreport.html + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + 파일 이름 인수에는 경로나 잘못된 문자가 포함되면 안 됩니다. 예: --report-html-filename myreport.html diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf index 7514281a8e..0ee4f4915c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Nazwa wygenerowanego raportu HTML + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Nazwa wygenerowanego raportu HTML @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - argument nazwy pliku nie może zawierać ścieżki ani nieprawidłowych znaków (np. --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + argument nazwy pliku nie może zawierać ścieżki ani nieprawidłowych znaków (np. --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf index 4f8d2b4c8f..cc14512d42 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - O nome do relatório HTML gerado + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + O nome do relatório HTML gerado @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - o argumento de nome de arquivo não deve conter um caminho ou caracteres inválidos (por exemplo, --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + o argumento de nome de arquivo não deve conter um caminho ou caracteres inválidos (por exemplo, --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf index 42a1d588ed..50a2d8fed5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Имя сгенерированного отчета в формате HTML + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Имя сгенерированного отчета в формате HTML @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - аргумент с именем файла не должен содержать путь или недопустимые символы (например: --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + аргумент с именем файла не должен содержать путь или недопустимые символы (например: --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf index f6271ce7d2..199e591854 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - Oluşturulan HTML raporunun adı + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + Oluşturulan HTML raporunun adı @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - Dosya adı bağımsız değişkeni yol veya geçersiz karakterler içermemelidir (ör. --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + Dosya adı bağımsız değişkeni yol veya geçersiz karakterler içermemelidir (ör. --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf index 1d7e4bbf9f..42d6a34911 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - 生成的 HTML 报表的名称 + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + 生成的 HTML 报表的名称 @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - 文件名参数不得包含路径或无效字符(例如 --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + 文件名参数不得包含路径或无效字符(例如 --report-html-filename myreport.html) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf index 1401b444dc..66ac561b71 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -23,8 +23,10 @@ - The name of the generated HTML report - 產生的 TRX 報告名稱 + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. +Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). +Example: MyReport_{tfm}.html + 產生的 TRX 報告名稱 @@ -33,8 +35,8 @@ - file name argument must not contain a path or invalid characters (e.g. --report-html-filename myreport.html) - 檔案名稱引數不能包含路徑或無效字元 (例如 --report-html-filename myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) + 檔案名稱引數不能包含路徑或無效字元 (例如 --report-html-filename myreport.html) diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs index 58f22d91e8..3dca2a49a1 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs @@ -133,7 +133,9 @@ Override the Azure DevOps artifact container name. Defaults to 'TestResults_{ass --report-html Enable generating an HTML report --report-html-filename - The name of the generated HTML report + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. + Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). + Example: MyReport_{tfm}.html --report-trx Enable generating TRX report --report-trx-filename diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HtmlReportTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HtmlReportTests.cs index b0aa16bc58..2beb8c0858 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HtmlReportTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HtmlReportTests.cs @@ -82,15 +82,29 @@ public async Task Html_WhenReportHtmlFilenameIsSpecified_HtmlReportIsGeneratedWi [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] [TestMethod] - public async Task Html_WhenReportHtmlFilenameContainsPath_ErrorIsDisplayed(string tfm) + public async Task Html_WhenReportHtmlFilenameContainsPath_HtmlReportIsGeneratedInThatPath(string tfm) { + string customFileName = Path.Combine("subdir", "report.html"); + string testResultsPath = Path.Combine(AssetFixture.TargetAssetPath, "bin", "Release", tfm, "TestResults"); + string customFilePath = Path.Combine(testResultsPath, customFileName); + string expectedFilePath = Regex.Escape(customFilePath); + var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm); TestHostResult testHostResult = await testHost.ExecuteAsync( - $"--report-html --report-html-filename {Path.Combine("subdir", "report.html")}", + $"--report-html --report-html-filename {customFileName}", cancellationToken: TestContext.CancellationToken); - testHostResult.AssertExitCodeIs(ExitCode.InvalidCommandLine); - testHostResult.AssertOutputContains("file name argument must not contain a path or invalid characters"); + testHostResult.AssertExitCodeIs(ExitCode.Success); + + string outputPattern = $""" + In process file artifacts produced: + - {expectedFilePath} +"""; + testHostResult.AssertOutputMatchesRegex(outputPattern); + + Assert.IsTrue( + File.Exists(customFilePath), + $"Expected custom HTML report file '{customFileName}' was not found in '{testResultsPath}'."); } [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))] diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs index 7fae073d0e..d3a43494f3 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs @@ -332,6 +332,89 @@ public async Task GenerateReportAsync_DefaultFileName_IncludesModuleNameAndTarge Assert.IsTrue(Regex.IsMatch(Path.GetFileName(finalPath), ExpectedFileNamePattern)); } + [TestMethod] + public async Task GenerateReportAsync_ExplicitRelativePath_IsResolvedUnderResultsDirectory() + { + string[]? htmlFileName = [Path.Combine("nested", "custom.html")]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName, out htmlFileName)).Returns(true); + + string? pathSeen = null; + var directories = new List(); + _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); + _ = _fileSystem.Setup(x => x.CreateDirectory(It.IsAny())) + .Callback(directories.Add) + .Returns(path => path); + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Create)) + .Returns((path, _) => + { + pathSeen = path; + return new MemoryFileStream(); + }); + + HtmlReportEngine engine = CreateEngine(); + _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns("out"); + + (string finalPath, _) = await engine.GenerateReportAsync([Captured("a", "A", "passed")]); + + string expectedPath = Path.Combine("out", "nested", "custom.html"); + Assert.AreEqual(expectedPath, finalPath); + Assert.AreEqual(expectedPath, pathSeen); + Assert.Contains(Path.Combine("out", "nested"), directories); + } + + [TestMethod] + public async Task GenerateReportAsync_ExplicitAbsolutePath_OverridesResultsDirectory() + { + string absolutePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".html"); + string[]? htmlFileName = [absolutePath]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName, out htmlFileName)).Returns(true); + + string? pathSeen = null; + _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); + _ = _fileSystem.Setup(x => x.CreateDirectory(It.IsAny())).Returns(path => path); + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Create)) + .Returns((path, _) => + { + pathSeen = path; + return new MemoryFileStream(); + }); + + HtmlReportEngine engine = CreateEngine(); + _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns("out"); + + (string finalPath, _) = await engine.GenerateReportAsync([Captured("a", "A", "passed")]); + + Assert.AreEqual(absolutePath, finalPath); + Assert.AreEqual(absolutePath, pathSeen); + } + + [TestMethod] + public async Task GenerateReportAsync_ExplicitFileName_ResolvesPlaceholdersAndSanitizesLeafName() + { + string[]? htmlFileName = [Path.Combine("nested", "report*_{pid}_{tfm}.html")]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName, out htmlFileName)).Returns(true); + + string? pathSeen = null; + _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); + _ = _fileSystem.Setup(x => x.CreateDirectory(It.IsAny())).Returns(path => path); + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Create)) + .Returns((path, _) => + { + pathSeen = path; + return new MemoryFileStream(); + }); + + HtmlReportEngine engine = CreateEngine(); + _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns("out"); + _ = _environmentMock.SetupGet(_ => _.ProcessId).Returns(1234); + + (string finalPath, _) = await engine.GenerateReportAsync([Captured("a", "A", "passed")]); + + Assert.AreEqual(pathSeen, finalPath); + Assert.StartsWith("report__1234_net", Path.GetFileName(finalPath)); + Assert.EndsWith(".html", finalPath); + } + [TestMethod] public async Task GenerateReportAsync_AppendsDisambiguatingSuffix_When_DefaultFileExists() { @@ -476,6 +559,11 @@ private HtmlReportEngine CreateEngine(MemoryFileStream stream) _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), It.IsAny())).Returns(stream); + return CreateEngine(); + } + + private HtmlReportEngine CreateEngine() + { _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns(string.Empty); _ = _environmentMock.SetupGet(_ => _.MachineName).Returns("MachineName"); _ = _environmentMock.Setup(_ => _.GetEnvironmentVariable(It.IsAny())).Returns("user"); diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs index 1cace2d6f0..ebbd6a4b2e 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs @@ -11,13 +11,30 @@ namespace Microsoft.Testing.Extensions.UnitTests; public sealed class HtmlReportGeneratorCommandLineTests { [TestMethod] - public async Task IsValid_If_PureHtmlFileName_Is_Provided() + [DataRow("report.html")] + [DataRow("sub/report.html")] + [DataRow("sub\\report.html")] + public async Task IsValid_If_HtmlFileNameOrNestedPath_Is_Provided(string fileName) + { + var provider = new HtmlReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() + .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); + + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [fileName]).ConfigureAwait(false); + + Assert.IsTrue(result.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(result.ErrorMessage)); + } + + [TestMethod] + public async Task IsValid_If_HtmlFile_Has_Absolute_Path() { var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); + string fileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".html"); - ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["report.html"]).ConfigureAwait(false); + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [fileName]).ConfigureAwait(false); Assert.IsTrue(result.IsValid); Assert.IsTrue(string.IsNullOrEmpty(result.ErrorMessage)); @@ -40,15 +57,9 @@ public async Task IsInvalid_If_FileName_Does_Not_End_With_Html(string fileName) } [TestMethod] - [DataRow("sub/report.html")] - [DataRow("sub\\report.html")] - [DataRow("..\\report.html")] [DataRow("../report.html")] - [DataRow("..report.html")] // contains ".." - [DataRow("C:report.html")] // drive letter - [DataRow(" report.html")] // leading whitespace - [DataRow("report.html ")] // trailing whitespace - public async Task IsInvalid_If_FileName_Contains_Path_Or_Invalid_Chars(string fileName) + [DataRow("nested/../report.html")] + public async Task IsInvalid_If_RelativePath_Escapes_TestResultsDirectory(string fileName) { var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() @@ -60,6 +71,20 @@ public async Task IsInvalid_If_FileName_Contains_Path_Or_Invalid_Chars(string fi Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameShouldNotContainPath, result.ErrorMessage); } + [TestMethod] + [OSCondition(OperatingSystems.Windows)] + public async Task IsInvalid_If_HtmlFile_Uses_DriveRelativePath_OnWindows() + { + var provider = new HtmlReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() + .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); + + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["C:report.html"]).ConfigureAwait(false); + + Assert.IsFalse(result.IsValid); + Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameShouldNotContainPath, result.ErrorMessage); + } + [TestMethod] public async Task IsInvalid_If_FileName_Provided_Without_HtmlReport_Flag() { @@ -106,33 +131,9 @@ public async Task IsValid_When_HtmlReport_Used_Alone() } [TestMethod] - [DataRow("report*.html")] // * is Windows-invalid even though Linux allows it - [DataRow("report?.html")] // ? same - [DataRow("report\".html")] - [DataRow("report<.html")] - [DataRow("report>.html")] - [DataRow("report|.html")] - public async Task IsInvalid_When_FileName_Contains_WindowsInvalidChars_OnAnyOS(string fileName) - { - var provider = new HtmlReportGeneratorCommandLine(); - Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() - .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); - - ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [fileName]).ConfigureAwait(false); - - Assert.IsFalse(result.IsValid); - Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameShouldNotContainPath, result.ErrorMessage); - } - - [TestMethod] + [DataRow("report*.html")] [DataRow("CON.html")] - [DataRow("con.html")] // case insensitive - [DataRow("NUL.html")] - [DataRow("PRN.html")] - [DataRow("AUX.html")] - [DataRow("COM1.html")] - [DataRow("LPT9.html")] - public async Task IsInvalid_When_FileName_Is_Reserved_Windows_Device_Name(string fileName) + public async Task IsValid_When_FileName_WillBeSanitized_AtWriteTime(string fileName) { var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() @@ -140,20 +141,6 @@ public async Task IsInvalid_When_FileName_Is_Reserved_Windows_Device_Name(string ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [fileName]).ConfigureAwait(false); - Assert.IsFalse(result.IsValid); - Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameShouldNotContainPath, result.ErrorMessage); - } - - [TestMethod] - public async Task IsValid_When_FileName_Starts_With_Reserved_Name_But_Has_Extra_Chars() - { - // "CONfig.html" is not a reserved device name (only the bare "CON" base name is). - var provider = new HtmlReportGeneratorCommandLine(); - Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() - .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); - - ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["CONfig.html"]).ConfigureAwait(false); - Assert.IsTrue(result.IsValid); } } From 81bb8c524ab4ca852eecab31cd073d7f3f5be4eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 May 2026 16:07:21 +0000 Subject: [PATCH 3/5] Address HTML filename validation review feedback Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com> --- .../HtmlReportEngine.cs | 12 +++++++- .../HtmlReportGeneratorCommandLine.cs | 15 +++++++++- .../xlf/ExtensionResources.zh-Hant.xlf | 2 +- .../HtmlReportEngineTests.cs | 30 ++++++++++++++++++- .../HtmlReportGeneratorCommandLineTests.cs | 3 +- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs index 21c44a812b..6b3471f7ba 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportEngine.cs @@ -65,10 +65,13 @@ public HtmlReportEngine( out string[]? providedFileName); string fileName = fileNameExplicitlyProvided - ? ResolveHtmlFileName(providedFileName![0]) + ? ResolveHtmlFileName(GetProvidedFileName(providedFileName)) : BuildDefaultFileName(finishTime); string outputDirectory = _configuration.GetTestResultDirectory(); + // Path.Combine short-circuits when the second argument is rooted, so an absolute + // user-provided file name overrides the test results directory while validated + // relative paths stay nested under it. string finalPath = Path.Combine(outputDirectory, fileName); string? finalDirectory = Path.GetDirectoryName(finalPath); if (!RoslynString.IsNullOrEmpty(finalDirectory)) @@ -88,6 +91,11 @@ public HtmlReportEngine( return await WriteWithRetryAsync(finalPath, bytes, fileNameExplicitlyProvided).ConfigureAwait(false); } + private static string GetProvidedFileName(string[]? providedFileName) + => providedFileName is { Length: > 0 } + ? providedFileName[0] + : throw ApplicationStateGuard.Unreachable(); + private async Task<(string FileName, string? Warning)> WriteWithRetryAsync(string finalPath, byte[] bytes, bool fileNameExplicitlyProvided) { // Explicit file names: use FileMode.Create (overwrite). Default-generated file @@ -197,6 +205,8 @@ private static string ReplaceInvalidFileNameChars(string fileName) } private static bool IsInvalidFileNameChar(char c) + // Keep the explicit file-name sanitization aligned with TRX report naming so + // placeholders and cross-platform reserved characters produce compatible names. => c is < ' ' or '"' or '<' or '>' or '|' or ':' or '*' or '?' or '\\' or '/' or '@' or '(' or ')' or '^' or ' '; private static bool IsReservedFileName(string fileName) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs index 18d9c5dae0..60ae1c5255 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs @@ -78,9 +78,16 @@ public Task ValidateCommandLineOptionsAsync(ICommandLineOption private static bool EscapesResultsDirectory(string path) => !IsPathFullyQualified(path) - && (Path.IsPathRooted(path) + && (IsDriveRelativePath(path) + || Path.IsPathRooted(path) || path.Split(DirectorySeparators, StringSplitOptions.RemoveEmptyEntries).Any(segment => segment == "..")); + private static bool IsDriveRelativePath(string path) + => path.Length >= 2 + && IsValidDriveLetter(path[0]) + && path[1] == ':' + && (path.Length == 2 || !IsAnyDirectorySeparator(path[2])); + private static bool IsPathFullyQualified(string path) { #if NETCOREAPP @@ -102,4 +109,10 @@ static bool IsValidDriveLetter(char c) => c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z'); #endif } + + private static bool IsAnyDirectorySeparator(char c) + => c is '/' or '\\'; + + private static bool IsValidDriveLetter(char c) + => c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z'); } diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf index 66ac561b71..24b32f923b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -26,7 +26,7 @@ The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). Example: MyReport_{tfm}.html - 產生的 TRX 報告名稱 + 產生的 HTML 報告名稱 diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs index d3a43494f3..371a06c49e 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportEngineTests.cs @@ -411,10 +411,38 @@ public async Task GenerateReportAsync_ExplicitFileName_ResolvesPlaceholdersAndSa (string finalPath, _) = await engine.GenerateReportAsync([Captured("a", "A", "passed")]); Assert.AreEqual(pathSeen, finalPath); - Assert.StartsWith("report__1234_net", Path.GetFileName(finalPath)); + string finalFileName = Path.GetFileName(finalPath); + Assert.StartsWith("report__", finalFileName); + Assert.Contains("1234", finalFileName); + Assert.Contains("_net", finalFileName); Assert.EndsWith(".html", finalPath); } + [TestMethod] + public async Task GenerateReportAsync_ExplicitReservedFileName_SanitizesLeafName() + { + string[]? htmlFileName = [Path.Combine("nested", "CON.html")]; + _ = _commandLineOptionsMock.Setup(_ => _.TryGetOptionArgumentList(HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName, out htmlFileName)).Returns(true); + + string? pathSeen = null; + _ = _fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(false); + _ = _fileSystem.Setup(x => x.CreateDirectory(It.IsAny())).Returns(path => path); + _ = _fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Create)) + .Returns((path, _) => + { + pathSeen = path; + return new MemoryFileStream(); + }); + + HtmlReportEngine engine = CreateEngine(); + _ = _configurationMock.SetupGet(_ => _[It.IsAny()]).Returns("out"); + + (string finalPath, _) = await engine.GenerateReportAsync([Captured("a", "A", "passed")]); + + Assert.AreEqual(pathSeen, finalPath); + Assert.AreEqual("_CON.html", Path.GetFileName(finalPath)); + } + [TestMethod] public async Task GenerateReportAsync_AppendsDisambiguatingSuffix_When_DefaultFileExists() { diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs index ebbd6a4b2e..f8e647335d 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs @@ -72,8 +72,7 @@ public async Task IsInvalid_If_RelativePath_Escapes_TestResultsDirectory(string } [TestMethod] - [OSCondition(OperatingSystems.Windows)] - public async Task IsInvalid_If_HtmlFile_Uses_DriveRelativePath_OnWindows() + public async Task IsInvalid_If_HtmlFile_Uses_DriveRelativePath() { var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() From 28163629c88d1d91771e7139cf0c7479150dc927 Mon Sep 17 00:00:00 2001 From: Evangelink Date: Mon, 25 May 2026 13:32:31 +0200 Subject: [PATCH 4/5] Align HTML filename validation with TRX and fix --info expectation - Drop the cross-platform IsDriveRelativePath heuristic from HtmlReportGeneratorCommandLine: on Unix ':' is a valid filename character and rejecting C:report.html there diverged from --report-trx-filename. Use Path.IsPathRooted alone (Windows-only in practice) to mirror TRX behavior. - Split the nested-path data row that used '\\' into a Windows-only test so the assertion no longer implies cross-platform separator semantics that aren't implemented. - Rename the '..' validation test to reflect the actual rule (any parent-directory segment is rejected; the path doesn't have to resolve outside the results directory). - Gate the drive-relative-path test to Windows, matching the TRX unit test layout. - Update the --info acceptance expectation for --report-html-filename to match the new option description (full path/placeholder contract), which was broken after the option description was expanded. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../HtmlReportGeneratorCommandLine.cs | 67 ++++++++++++------- .../HelpInfoAllExtensionsTests.cs | 4 +- .../HtmlReportGeneratorCommandLineTests.cs | 28 ++++++-- 3 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs index 60ae1c5255..5bc7542e7c 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs @@ -77,30 +77,57 @@ public Task ValidateCommandLineOptionsAsync(ICommandLineOption : ValidationResult.ValidTask; private static bool EscapesResultsDirectory(string path) - => !IsPathFullyQualified(path) - && (IsDriveRelativePath(path) - || Path.IsPathRooted(path) - || path.Split(DirectorySeparators, StringSplitOptions.RemoveEmptyEntries).Any(segment => segment == "..")); + { + // Fully-qualified paths (e.g. "C:\foo.html", "\\server\share\foo.html" or "/foo.html") are + // accepted as-is and validated by the OS when we open the file - the user explicitly opted + // out of writing under the test results directory. + if (IsPathFullyQualified(path)) + { + return false; + } - private static bool IsDriveRelativePath(string path) - => path.Length >= 2 - && IsValidDriveLetter(path[0]) - && path[1] == ':' - && (path.Length == 2 || !IsAnyDirectorySeparator(path[2])); + // Drive-relative paths on Windows such as "C:foo.html" are "rooted" but not fully qualified - + // they resolve against the current directory of the drive, which is unpredictable and would + // silently escape the test results directory. Reject them. On non-Windows OSes + // Path.IsPathRooted only returns true for paths starting with "/", which are already handled + // above, so this check is effectively Windows-only and matches the TRX option behavior. + if (Path.IsPathRooted(path)) + { + return true; + } + + // Any remaining ".." segment in a relative path would escape the test results directory. + return path.Split(DirectorySeparators, StringSplitOptions.RemoveEmptyEntries).Any(segment => segment == ".."); + } private static bool IsPathFullyQualified(string path) { #if NETCOREAPP return Path.IsPathFullyQualified(path); #else - return path.Length >= 2 - && ((IsDirectorySeparator(path[0]) && IsDirectorySeparator(path[1])) - || (Path.DirectorySeparatorChar == '/' - ? path[0] == '/' - : path.Length >= 3 - && IsValidDriveLetter(path[0]) - && path[1] == ':' - && IsDirectorySeparator(path[2]))); + // Mirrors the runtime implementation that is missing on .NET Framework and netstandard2.0. + if (path.Length < 2) + { + return false; + } + + // UNC paths like "\\server\share" (or with forward slashes). + if (IsDirectorySeparator(path[0]) && IsDirectorySeparator(path[1])) + { + return true; + } + + // On Unix, only paths starting with "/" are fully qualified. + if (Path.DirectorySeparatorChar == '/') + { + return path[0] == '/'; + } + + // On Windows, fully qualified drive paths must be "X:\" or "X:/". + return path.Length >= 3 + && IsValidDriveLetter(path[0]) + && path[1] == ':' + && IsDirectorySeparator(path[2]); static bool IsDirectorySeparator(char c) => c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; @@ -109,10 +136,4 @@ static bool IsValidDriveLetter(char c) => c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z'); #endif } - - private static bool IsAnyDirectorySeparator(char c) - => c is '/' or '\\'; - - private static bool IsValidDriveLetter(char c) - => c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z'); } diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs index 3dca2a49a1..db585d85e6 100644 --- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs +++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/HelpInfoAllExtensionsTests.cs @@ -414,7 +414,9 @@ Default type is 'Full' --report-html-filename Arity: 1 Hidden: False - Description: The name of the generated HTML report + Description: The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. + Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). + Example: MyReport_{tfm}.html MSBuildCommandLineProvider Name: MSBuildCommandLineProvider Version: * diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs index f8e647335d..c4ff513721 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs @@ -13,8 +13,7 @@ public sealed class HtmlReportGeneratorCommandLineTests [TestMethod] [DataRow("report.html")] [DataRow("sub/report.html")] - [DataRow("sub\\report.html")] - public async Task IsValid_If_HtmlFileNameOrNestedPath_Is_Provided(string fileName) + public async Task IsValid_If_HtmlFileNameOrNestedRelativePath_Is_Provided(string fileName) { var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() @@ -26,6 +25,22 @@ public async Task IsValid_If_HtmlFileNameOrNestedPath_Is_Provided(string fileNam Assert.IsTrue(string.IsNullOrEmpty(result.ErrorMessage)); } + [TestMethod] + [OSCondition(OperatingSystems.Windows)] + public async Task IsValid_If_HtmlFileNameUsesBackslashSeparator_OnWindows() + { + // The '\' character is only a directory separator on Windows; on Unix it would be treated + // as part of the leaf file name (and later sanitized at write time). + var provider = new HtmlReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() + .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); + + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["sub\\report.html"]).ConfigureAwait(false); + + Assert.IsTrue(result.IsValid); + Assert.IsTrue(string.IsNullOrEmpty(result.ErrorMessage)); + } + [TestMethod] public async Task IsValid_If_HtmlFile_Has_Absolute_Path() { @@ -59,7 +74,7 @@ public async Task IsInvalid_If_FileName_Does_Not_End_With_Html(string fileName) [TestMethod] [DataRow("../report.html")] [DataRow("nested/../report.html")] - public async Task IsInvalid_If_RelativePath_Escapes_TestResultsDirectory(string fileName) + public async Task IsInvalid_If_RelativePath_Contains_ParentDirectorySegment(string fileName) { var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() @@ -72,8 +87,13 @@ public async Task IsInvalid_If_RelativePath_Escapes_TestResultsDirectory(string } [TestMethod] - public async Task IsInvalid_If_HtmlFile_Uses_DriveRelativePath() + [OSCondition(OperatingSystems.Windows)] + public async Task IsInvalid_If_HtmlFile_Uses_DriveRelativePath_OnWindows() { + // Drive-relative paths such as "C:report.html" are "rooted" but not fully qualified, so they + // would silently escape the test results directory. Validate that they are rejected on Windows. + // On non-Windows OSes ':' is a valid file-name character, so this check is Windows-only and + // matches the TRX option behavior. var provider = new HtmlReportGeneratorCommandLine(); Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); From ea4781e1ea8c8548acb73fc9f1351afec9789b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Mon, 25 May 2026 15:25:36 +0200 Subject: [PATCH 5/5] Add dedicated HtmlReportFileNameMustNotBeEmpty resource Splits the HTML filename validation errors so that the empty-argument and empty-leaf cases use a dedicated 'must not be empty' message, matching the TRX resource layout (TrxReportFileNameMustNotBeEmpty vs TrxReportFileNameRelativePathMustStayUnderResultsDirectory). The existing 'should not contain path' resource is renamed to HtmlReportFileNameRelativePathMustStayUnderResultsDirectory so the keys, wording, and intent stay aligned with TRX. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../HtmlReportGeneratorCommandLine.cs | 6 ++-- .../Resources/ExtensionResources.resx | 9 +++-- .../Resources/xlf/ExtensionResources.cs.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.de.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.es.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.fr.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.it.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.ja.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.ko.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.pl.xlf | 15 ++++++--- .../xlf/ExtensionResources.pt-BR.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.ru.xlf | 15 ++++++--- .../Resources/xlf/ExtensionResources.tr.xlf | 15 ++++++--- .../xlf/ExtensionResources.zh-Hans.xlf | 15 ++++++--- .../xlf/ExtensionResources.zh-Hant.xlf | 15 ++++++--- .../HtmlReportGeneratorCommandLineTests.cs | 33 +++++++++++++++++-- 16 files changed, 170 insertions(+), 73 deletions(-) diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs index 5bc7542e7c..3635b156ee 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGeneratorCommandLine.cs @@ -44,7 +44,7 @@ public Task ValidateOptionArgumentsAsync(CommandLineOption com { if (arguments.Length is 0) { - return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameShouldNotContainPath); + return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameMustNotBeEmpty); } string argument = arguments[0]; @@ -52,7 +52,7 @@ public Task ValidateOptionArgumentsAsync(CommandLineOption com string fileNamePart = Path.GetFileName(argument); if (RoslynString.IsNullOrWhiteSpace(fileNamePart)) { - return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameShouldNotContainPath); + return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameMustNotBeEmpty); } if (!fileNamePart.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) @@ -62,7 +62,7 @@ public Task ValidateOptionArgumentsAsync(CommandLineOption com if (EscapesResultsDirectory(argument)) { - return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameShouldNotContainPath); + return ValidationResult.InvalidTask(ExtensionResources.HtmlReportFileNameRelativePathMustStayUnderResultsDirectory); } } diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx index 69c572d778..c00a8d5937 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/ExtensionResources.resx @@ -70,17 +70,20 @@ '--report-html-filename' file name argument must end with '.html' (e.g. --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). Example: MyReport_{tfm}.html + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' requires '--report-html' to be enabled - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - Produce a self-contained HTML report for the current test session diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf index bbbb2fce7a..a17c5265e5 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.cs.xlf @@ -22,6 +22,11 @@ Argument názvu souboru „--report-html-filename“ musí končit na .html (např. --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Název vygenerované sestavy HTML + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled „--report-html-filename“ vyžaduje povolení „--report-html“ - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - Argument názvu souboru nesmí obsahovat cestu nebo neplatné znaky (např. --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Vytvoření samostatné sestavy HTML pro aktuální testovací relaci diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf index 7f1f442d65..432d8694fc 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.de.xlf @@ -22,6 +22,11 @@ Das Dateinamenargument „--report-html-filename“ muss mit „.html“ enden (z. B. --report-html-filename myreport.html). + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Der Name des generierten HTML-Berichts. + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled „--report-html-filename“ erfordert, dass „--report-html“ aktiviert ist. - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - Das Dateinamenargument darf keinen Pfad und keine ungültigen Zeichen enthalten (z. B. --report-html-filename myreport.html). - - Produce a self-contained HTML report for the current test session Erstellt einen eigenständigen HTML-Bericht für die aktuelle Testsitzung diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf index 23741e9f4b..0246dbc161 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.es.xlf @@ -22,6 +22,11 @@ El argumento del nombre de archivo '--report-html-filename' debe terminar en '.html' (por ejemplo, --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Nombre del informe HTML generado + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled '--report-html-filename' requiere que '--report-html' esté habilitado - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - el argumento de nombre de archivo no debe contener una ruta de acceso ni caracteres no válidos (por ejemplo, --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Generar un informe HTML independiente para la sesión de prueba actual diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf index 9c140b4ada..da6612001d 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.fr.xlf @@ -22,6 +22,11 @@ L’argument de nom de fichier de « --report-html-filename » doit se terminer par « .html » (par exemple, --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Nom du rapport HTML généré + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled « --report-html-filename » nécessite l’activation de « --report-html » - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - L’argument de nom de fichier ne doit pas contenir de chemin d’accès ni de caractères non valides (par exemple, --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Générer un rapport HTML autonome pour la session de test actuelle diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf index 631b4751ec..a52ef541ea 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.it.xlf @@ -22,6 +22,11 @@ L'argomento del nome file "--report-html-filename" deve terminare con ".html" (ad esempio --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Nome del report HTML generato + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled "--report-html-filename" richiede che "--report-html" sia abilitato - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - l'argomento del nome file non deve contenere un percorso o caratteri non validi (ad esempio --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Generare un report HTML autonomo per la sessione di test corrente diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf index 2ba7456b17..7f2a4136e7 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ja.xlf @@ -22,6 +22,11 @@ '--report-html-filename' のファイル名引数の末尾は '.html' にする必要があります (例: --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html 生成された HTML レポートの名前 + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled '--report-html-filename' を使用するには、'--report-html' を有効にする必要があります - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - ファイル名引数にパスや無効な文字を含めることはできません (例: --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session 現在のテスト セッションの自己完結型 HTML レポートを生成する diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf index 75271a1b96..497d49067e 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ko.xlf @@ -22,6 +22,11 @@ '--report-html-filename' 파일 이름 인수는 '.html'로 끝나야 합니다. 예: --report-html-filename myreport.html + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html 생성된 HTML 보고서의 이름 + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled '--report-html-filename'을 사용하려면 '--report-html'을 사용하도록 설정해야 합니다. - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - 파일 이름 인수에는 경로나 잘못된 문자가 포함되면 안 됩니다. 예: --report-html-filename myreport.html - - Produce a self-contained HTML report for the current test session 현재 테스트 세션에 대한 독립 실행형 HTML 보고서 생성 diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf index 0ee4f4915c..f7ff23bceb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pl.xlf @@ -22,6 +22,11 @@ Argument nazwy pliku „--report-html-filename” musi kończyć się ciągiem „.html” (np. --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Nazwa wygenerowanego raportu HTML + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled Element „--report-html-filename” wymaga włączenia parametru „--report-html” - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - argument nazwy pliku nie może zawierać ścieżki ani nieprawidłowych znaków (np. --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Tworzenie samodzielnego raportu HTML dla bieżącej sesji testowej diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf index cc14512d42..0034bb5adb 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.pt-BR.xlf @@ -22,6 +22,11 @@ O argumento de nome de arquivo "--report-html-filename" deve terminar com ".html" (por exemplo, --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html O nome do relatório HTML gerado + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled "--report-html-filename" requer que "--report-html" esteja habilitado - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - o argumento de nome de arquivo não deve conter um caminho ou caracteres inválidos (por exemplo, --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Produzir um relatório HTML autossuficiente para a sessão de teste atual diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf index 50a2d8fed5..456b2115d8 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.ru.xlf @@ -22,6 +22,11 @@ Аргумент имени файла "--report-html-filename" должен оканчиваться на ".html" (например: --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Имя сгенерированного отчета в формате HTML + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled Для "--report-html-filename" требуется включить "--report-html" - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - аргумент с именем файла не должен содержать путь или недопустимые символы (например: --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Создать автономный отчет в формате HTML для текущего тестового сеанса diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf index 199e591854..dae3cea660 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.tr.xlf @@ -22,6 +22,11 @@ '--report-html-filename' dosya adı bağımsız değişkeni '.html' ile bitmelidir (ör. --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html Oluşturulan HTML raporunun adı + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled '--report-html-filename', '--report-html' seçeneğinin etkinleştirilmesini gerektirir - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - Dosya adı bağımsız değişkeni yol veya geçersiz karakterler içermemelidir (ör. --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session Geçerli test oturumu için bağımsız bir HTML raporu oluşturun diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf index 42d6a34911..9b52b1ac9b 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hans.xlf @@ -22,6 +22,11 @@ "--report-html-filename" 文件名参数必须以 ".html" 结尾(例如 --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html 生成的 HTML 报表的名称 + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled "--report-html-filename" 需要启用 "--report-html" - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - 文件名参数不得包含路径或无效字符(例如 --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session 为当前测试会话生成独立的 HTML 报表 diff --git a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf index 24b32f923b..8d0ba6c172 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf +++ b/src/Platform/Microsoft.Testing.Extensions.HtmlReport/Resources/xlf/ExtensionResources.zh-Hant.xlf @@ -22,6 +22,11 @@ '--report-html-filename' 檔名引數的結尾必須是 '.html' (例如 --report-html-filename myreport.html) + + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + '--report-html-filename' file name part must not be empty (e.g. --report-html-filename myreport.html) + + The name of the generated HTML report. May include a relative or absolute path; relative paths are resolved against the test results directory and missing directories are created. Supports the following placeholders: {pname} (test application name), {pid} (process ID), {asm} (entry assembly name), {tfm} (target framework moniker), {time} (timestamp). @@ -29,16 +34,16 @@ Example: MyReport_{tfm}.html 產生的 HTML 報告名稱 + + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename nested/myreport.html) + + '--report-html-filename' requires '--report-html' to be enabled '--report-html-filename' 需要啟用 '--report-html' - - '--report-html-filename' relative paths must stay under the test results directory (e.g. --report-html-filename myreport.html) - 檔案名稱引數不能包含路徑或無效字元 (例如 --report-html-filename myreport.html) - - Produce a self-contained HTML report for the current test session 為目前的測試工作階段產生獨立式 HTML 報告 diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs index c4ff513721..aa4fc14998 100644 --- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs +++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/HtmlReportGeneratorCommandLineTests.cs @@ -83,7 +83,7 @@ public async Task IsInvalid_If_RelativePath_Contains_ParentDirectorySegment(stri ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [fileName]).ConfigureAwait(false); Assert.IsFalse(result.IsValid); - Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameShouldNotContainPath, result.ErrorMessage); + Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameRelativePathMustStayUnderResultsDirectory, result.ErrorMessage); } [TestMethod] @@ -101,7 +101,36 @@ public async Task IsInvalid_If_HtmlFile_Uses_DriveRelativePath_OnWindows() ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, ["C:report.html"]).ConfigureAwait(false); Assert.IsFalse(result.IsValid); - Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameShouldNotContainPath, result.ErrorMessage); + Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameRelativePathMustStayUnderResultsDirectory, result.ErrorMessage); + } + + [TestMethod] + [DataRow(" ")] + [DataRow("sub/")] + [DataRow("sub/ ")] + public async Task IsInvalid_If_FileNamePart_Is_Empty_Or_Whitespace(string fileName) + { + var provider = new HtmlReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() + .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); + + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, [fileName]).ConfigureAwait(false); + + Assert.IsFalse(result.IsValid); + Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameMustNotBeEmpty, result.ErrorMessage); + } + + [TestMethod] + public async Task IsInvalid_If_No_Argument_Provided() + { + var provider = new HtmlReportGeneratorCommandLine(); + Platform.Extensions.CommandLine.CommandLineOption option = provider.GetCommandLineOptions() + .First(x => x.Name == HtmlReportGeneratorCommandLine.HtmlReportFileNameOptionName); + + ValidationResult result = await provider.ValidateOptionArgumentsAsync(option, []).ConfigureAwait(false); + + Assert.IsFalse(result.IsValid); + Assert.AreEqual(HtmlReport.Resources.ExtensionResources.HtmlReportFileNameMustNotBeEmpty, result.ErrorMessage); } [TestMethod]