internal class Attachments()

in TeamCity.VSTest.TestLogger/Attachments.cs [11:118]


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;
                }
            }
        }
    }
}