TeamCity.VSTest.TestLogger/Attachments.cs (106 lines of code) (raw):

namespace TeamCity.VSTest.TestLogger; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; using JetBrains.TeamCity.ServiceMessages.Write.Special; using Microsoft.VisualStudio.TestPlatform.ObjectModel; internal class Attachments( IOptions options, IIdGenerator idGenerator, ITeamCityWriter rootWriter, ITestAttachmentPathResolver testAttachmentPathResolver) : IAttachments { private static readonly Regex AttachmentDescriptionRegex = new("(.*)=>(.+)", RegexOptions.Compiled); private readonly IOptions _options = options ?? throw new ArgumentNullException(nameof(options)); private readonly IIdGenerator _idGenerator = idGenerator ?? throw new ArgumentNullException(nameof(idGenerator)); private readonly ITeamCityWriter _rootWriter = rootWriter ?? throw new ArgumentNullException(nameof(rootWriter)); public void SendAttachment(string testName, UriDataAttachment attachment, ITeamCityTestWriter testWriter) { if (testName == null) throw new ArgumentNullException(nameof(testName)); if (attachment == null) throw new ArgumentNullException(nameof(attachment)); if (testWriter == null) throw new ArgumentNullException(nameof(testWriter)); if (!_options.MetadataEnable || !_options.AllowExperimental || _options.Version.CompareTo(_options.TestMetadataSupportVersion) < 0) { testWriter.WriteStdOutput($"Attachment \"{attachment.Description}\": \"{attachment.Uri}\""); return; } if (!attachment.Uri.IsFile) { return; } var filePath = attachment.Uri.LocalPath; if (string.IsNullOrEmpty(filePath)) { return; } var description = attachment.Description ?? string.Empty; if (description == filePath) { description = string.Empty; } var fileName = Path.GetFileName(filePath); var fileExtension = Path.GetExtension(fileName).ToLowerInvariant(); string? artifactDir = default; if (!string.IsNullOrEmpty(description)) { var match = AttachmentDescriptionRegex.Match(description); if (match.Success) { description = match.Groups[1].Value.Trim(); artifactDir = match.Groups[2].Value.Trim(); } } if (artifactDir == null) { var normalized = new string(NormalizeTestName(testName).ToArray()); var testDirName = testAttachmentPathResolver.Resolve(normalized); artifactDir = ".teamcity/VSTest/" + testDirName + "/" + _idGenerator.NewId(); } _rootWriter.PublishArtifact(filePath + " => " + artifactDir); var artifact = artifactDir + "/" + fileName; switch (fileExtension) { case ".bmp": case ".gif": case ".ico": case ".jng": case ".jpeg": case ".jpg": case ".jfif": case ".jp2": case ".jps": case ".tga": case ".tiff": case ".tif": case ".svg": case ".wmf": case ".emf": case ".png": testWriter.WriteImage(artifact, description); break; default: testWriter.WriteFile(artifact, description); break; } } private static IEnumerable<char> NormalizeTestName(IEnumerable<char> testName) { foreach (var ch in testName) { if (char.IsLetterOrDigit(ch) || char.IsWhiteSpace(ch)) { yield return ch; } else { foreach (var chr in $"{(ulong)ch:x4}") { yield return chr; } } } } }