From a154209660cc552389559cc8240cf807f75c2bae Mon Sep 17 00:00:00 2001 From: Puneet Agarwal Date: Wed, 23 Oct 2024 15:38:08 +0530 Subject: [PATCH 1/3] Add support to upload attachments --- .../src/Processor/TestProcessor.cs | 108 ++++++++++++++++-- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs index db8142ca2a40..95ae48a0663d 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs @@ -11,6 +11,7 @@ using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Model; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Utility; +using Azure.Identity; using Azure.Storage.Blobs; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; @@ -34,10 +35,13 @@ internal class TestProcessor : ITestProcessor internal int PassedTestCount { get; set; } = 0; internal int FailedTestCount { get; set; } = 0; internal int SkippedTestCount { get; set; } = 0; + internal int TotalArtifactCount { get; set; } = 0; + internal int TotalArtifactSize { get; set; } = 0; internal List TestResults { get; set; } = new List(); internal ConcurrentDictionary RawTestResultsMap { get; set; } = new(); internal bool FatalTestExecution { get; set; } = false; internal TestRunShardDto? _testRunShard; + internal TestResultsUri? _testResultsSasUri; public TestProcessor(CloudRunMetadata cloudRunMetadata, CIInfo cIInfo, ILogger? logger = null, IDataProcessor? dataProcessor = null, ICloudRunErrorParser? cloudRunErrorParser = null, IServiceClient? serviceClient = null, IConsoleWriter? consoleWriter = null) { @@ -91,17 +95,25 @@ public void TestCaseResultHandler(object? sender, TestResultEventArgs e) try { TestResult testResultSource = e.Result; - TestResults? testResult = _dataProcessor.GetTestCaseResultData(testResultSource); - RawTestResult rawResult = DataProcessor.GetRawResultObject(testResultSource); - RawTestResultsMap.TryAdd(testResult.TestExecutionId, rawResult); // TODO - Send error to blob _cloudRunErrorParser.HandleScalableRunErrorMessage(testResultSource.ErrorMessage); _cloudRunErrorParser.HandleScalableRunErrorMessage(testResultSource.ErrorStackTrace); - if (!_cloudRunMetadata.EnableResultPublish) + if (!_cloudRunMetadata.EnableResultPublish || FatalTestExecution) { return; } + + TestResults? testResult = _dataProcessor.GetTestCaseResultData(testResultSource); + RawTestResult rawResult = DataProcessor.GetRawResultObject(testResultSource); + + // TODO move rawResult upload here same as JS + RawTestResultsMap.TryAdd(testResult.TestExecutionId, rawResult); + + // Upload Attachments + UploadAttachment(e, testResult.TestExecutionId); + + // Update Test Count if (testResult != null) { TotalTestCount++; @@ -143,7 +155,7 @@ public void TestRunCompleteHandler(object? sender, TestRunCompleteEventArgs e) } try { - TestResultsUri? sasUri = _serviceClient.GetTestRunResultsUri(); + TestResultsUri? sasUri = CheckAndRenewSasUri(); if (!string.IsNullOrEmpty(sasUri?.Uri)) { foreach (TestResults testResult in TestResults) @@ -151,12 +163,7 @@ public void TestRunCompleteHandler(object? sender, TestRunCompleteEventArgs e) if (RawTestResultsMap.TryGetValue(testResult.TestExecutionId!, out RawTestResult? rawResult) && rawResult != null) { // Renew the SAS URI if needed - var reporterUtils = new ReporterUtils(); - if (sasUri == null || !reporterUtils.IsTimeGreaterThanCurrentPlus10Minutes(sasUri.Uri)) - { - sasUri = _serviceClient.GetTestRunResultsUri(); // Create new SAS URI - _logger.Info($"Fetched SAS URI with validity: {sasUri?.ExpiresAt} and access: {sasUri?.AccessLevel}."); - } + sasUri = CheckAndRenewSasUri(); if (sasUri == null) { _logger.Warning("SAS URI is empty"); @@ -189,6 +196,70 @@ public void TestRunCompleteHandler(object? sender, TestRunCompleteEventArgs e) } #region Test Processor Helper Methods + + private void UploadAttachment(TestResultEventArgs e, string testExecutionId) + { + _testResultsSasUri = CheckAndRenewSasUri(); + if (e.Result.Attachments != null) + { + foreach (var attachmentSet in e.Result.Attachments) + { + foreach (var attachmentData in attachmentSet.Attachments) + { + var filePath = attachmentData.Uri.LocalPath; + _logger.Info($"Uploading attachment: {filePath}"); + if (!File.Exists( filePath )) + { + _logger.Error($"Attachment file not found: {filePath}"); + continue; + } + try + { + // get file size + var fileSize = new FileInfo(filePath).Length; + var cloudFileName = GetCloudFileName(filePath, testExecutionId); + if (cloudFileName != null) { + UploadBlobFile(_testResultsSasUri!.Uri!, cloudFileName, filePath); + TotalArtifactCount++; + TotalArtifactSize = TotalArtifactSize + (int)fileSize; + } + else + { + _logger.Error($"Attachment file Upload Failed: {filePath}"); + } + } + catch (Exception exp) + { + var error = $"Cannot Upload '{filePath}' file: {exp.Message}"; + + _logger.Error(error); + } + } + } + } + } + + private string? GetCloudFileName(string filePath, string testExecutionId) + { + var fileName = Path.GetFileName(filePath); + if (fileName == null) + { + return null; + } + return $"{testExecutionId}/{fileName}"; // TODO check if we need to add {Guid.NewGuid()} for file with same name + } + + private TestResultsUri? CheckAndRenewSasUri() + { + var reporterUtils = new ReporterUtils(); + if (_testResultsSasUri == null || !reporterUtils.IsTimeGreaterThanCurrentPlus10Minutes(_testResultsSasUri.Uri)) + { + _testResultsSasUri = _serviceClient.GetTestRunResultsUri(); + _logger.Info($"Fetched SAS URI with validity: {_testResultsSasUri?.ExpiresAt} and access: {_testResultsSasUri?.AccessLevel}."); + } + return _testResultsSasUri; + } + private void EndTestRun(TestRunCompleteEventArgs e) { if (_cloudRunMetadata.EnableResultPublish && !FatalTestExecution) @@ -227,6 +298,19 @@ private void UploadBuffer(string uri, string buffer, string fileRelativePath) blobClient.Upload(new BinaryData(bufferBytes), overwrite: true); _logger.Info($"Uploaded buffer to {fileRelativePath}"); } + + private void UploadBlobFile(string uri, string fileRelativePath, string filePath) + { + string cloudFilePath = GetCloudFilePath(uri, fileRelativePath); + BlobClient blobClient = new(new Uri(cloudFilePath)); + // Upload filePath to Blob + blobClient.Upload(filePath, overwrite: true); + + //byte[] bufferBytes = File.ReadAllBytes(fileRelativePath); + //blobClient.Upload(new BinaryData(bufferBytes), overwrite: true); + _logger.Info($"Uploaded file {filePath} to {fileRelativePath}"); + } + private TestRunShardDto GetTestRunEndShard(TestRunCompleteEventArgs e) { DateTime testRunEndedOn = DateTime.UtcNow; @@ -247,7 +331,7 @@ private TestRunShardDto GetTestRunEndShard(TestRunCompleteEventArgs e) testRunShard.Summary.StartTime = _cloudRunMetadata.TestRunStartTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); testRunShard.Summary.EndTime = testRunEndedOn.ToString("yyyy-MM-ddTHH:mm:ssZ"); testRunShard.Summary.TotalTime = durationInMs; - testRunShard.Summary.UploadMetadata = new UploadMetadata() { NumTestResults = TotalTestCount, NumTotalAttachments = 0, SizeTotalAttachments = 0 }; + testRunShard.Summary.UploadMetadata = new UploadMetadata() { NumTestResults = TotalTestCount, NumTotalAttachments = TotalArtifactCount, SizeTotalAttachments = TotalArtifactSize }; testRunShard.UploadCompleted = true; return testRunShard; } From ff0477a3da56252f50db5bc7abb830e06d4457e7 Mon Sep 17 00:00:00 2001 From: Puneet Agarwal Date: Wed, 23 Oct 2024 17:18:57 +0530 Subject: [PATCH 2/3] fix tests --- .../src/Processor/TestProcessor.cs | 5 ++--- .../tests/Processor/TestProcessorTests.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs index 95ae48a0663d..12a39c70ce94 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs @@ -95,6 +95,8 @@ public void TestCaseResultHandler(object? sender, TestResultEventArgs e) try { TestResult testResultSource = e.Result; + TestResults? testResult = _dataProcessor.GetTestCaseResultData(testResultSource); + RawTestResult rawResult = DataProcessor.GetRawResultObject(testResultSource); // TODO - Send error to blob _cloudRunErrorParser.HandleScalableRunErrorMessage(testResultSource.ErrorMessage); @@ -104,9 +106,6 @@ public void TestCaseResultHandler(object? sender, TestResultEventArgs e) return; } - TestResults? testResult = _dataProcessor.GetTestCaseResultData(testResultSource); - RawTestResult rawResult = DataProcessor.GetRawResultObject(testResultSource); - // TODO move rawResult upload here same as JS RawTestResultsMap.TryAdd(testResult.TestExecutionId, rawResult); diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs index 2ebe866cc5f2..652f73195b3e 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/tests/Processor/TestProcessorTests.cs @@ -355,7 +355,7 @@ public void TestCaseResultHandler_EnableResultPublishFalse_OnlyParseScalableErro testProcessor.TestCaseResultHandler(sender, new TestResultEventArgs(testResult)); Assert.AreEqual(0, testProcessor.TestResults.Count); - Assert.IsTrue(testProcessor.RawTestResultsMap.Keys.Count == 1); + Assert.IsTrue(testProcessor.RawTestResultsMap.Keys.Count == 0); Assert.IsTrue(testProcessor.PassedTestCount == 0); Assert.IsTrue(testProcessor.FailedTestCount == 0); Assert.IsTrue(testProcessor.SkippedTestCount == 0); From d96b4c610cd235911e4d1533f1f68e5c0d69433e Mon Sep 17 00:00:00 2001 From: Puneet Agarwal Date: Thu, 24 Oct 2024 15:16:41 +0530 Subject: [PATCH 3/3] minor comments fixes --- .../src/Processor/TestProcessor.cs | 26 +++++-------------- .../src/Utility/ReporterUtils.cs | 10 +++++++ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs index 12a39c70ce94..d90affe987a6 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Processor/TestProcessor.cs @@ -11,7 +11,6 @@ using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Interface; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Model; using Azure.Developer.MicrosoftPlaywrightTesting.TestLogger.Utility; -using Azure.Identity; using Azure.Storage.Blobs; using Microsoft.VisualStudio.TestPlatform.ObjectModel; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; @@ -36,7 +35,7 @@ internal class TestProcessor : ITestProcessor internal int FailedTestCount { get; set; } = 0; internal int SkippedTestCount { get; set; } = 0; internal int TotalArtifactCount { get; set; } = 0; - internal int TotalArtifactSize { get; set; } = 0; + internal int TotalArtifactSizeInBytes { get; set; } = 0; internal List TestResults { get; set; } = new List(); internal ConcurrentDictionary RawTestResultsMap { get; set; } = new(); internal bool FatalTestExecution { get; set; } = false; @@ -216,20 +215,20 @@ private void UploadAttachment(TestResultEventArgs e, string testExecutionId) { // get file size var fileSize = new FileInfo(filePath).Length; - var cloudFileName = GetCloudFileName(filePath, testExecutionId); + var cloudFileName = ReporterUtils.GetCloudFileName(filePath, testExecutionId); if (cloudFileName != null) { UploadBlobFile(_testResultsSasUri!.Uri!, cloudFileName, filePath); TotalArtifactCount++; - TotalArtifactSize = TotalArtifactSize + (int)fileSize; + TotalArtifactSizeInBytes = TotalArtifactSizeInBytes + (int)fileSize; } else { _logger.Error($"Attachment file Upload Failed: {filePath}"); } } - catch (Exception exp) + catch (Exception ex) { - var error = $"Cannot Upload '{filePath}' file: {exp.Message}"; + var error = $"Cannot Upload '{filePath}' file: {ex.Message}"; _logger.Error(error); } @@ -238,16 +237,6 @@ private void UploadAttachment(TestResultEventArgs e, string testExecutionId) } } - private string? GetCloudFileName(string filePath, string testExecutionId) - { - var fileName = Path.GetFileName(filePath); - if (fileName == null) - { - return null; - } - return $"{testExecutionId}/{fileName}"; // TODO check if we need to add {Guid.NewGuid()} for file with same name - } - private TestResultsUri? CheckAndRenewSasUri() { var reporterUtils = new ReporterUtils(); @@ -304,9 +293,6 @@ private void UploadBlobFile(string uri, string fileRelativePath, string filePath BlobClient blobClient = new(new Uri(cloudFilePath)); // Upload filePath to Blob blobClient.Upload(filePath, overwrite: true); - - //byte[] bufferBytes = File.ReadAllBytes(fileRelativePath); - //blobClient.Upload(new BinaryData(bufferBytes), overwrite: true); _logger.Info($"Uploaded file {filePath} to {fileRelativePath}"); } @@ -330,7 +316,7 @@ private TestRunShardDto GetTestRunEndShard(TestRunCompleteEventArgs e) testRunShard.Summary.StartTime = _cloudRunMetadata.TestRunStartTime.ToString("yyyy-MM-ddTHH:mm:ssZ"); testRunShard.Summary.EndTime = testRunEndedOn.ToString("yyyy-MM-ddTHH:mm:ssZ"); testRunShard.Summary.TotalTime = durationInMs; - testRunShard.Summary.UploadMetadata = new UploadMetadata() { NumTestResults = TotalTestCount, NumTotalAttachments = TotalArtifactCount, SizeTotalAttachments = TotalArtifactSize }; + testRunShard.Summary.UploadMetadata = new UploadMetadata() { NumTestResults = TotalTestCount, NumTotalAttachments = TotalArtifactCount, SizeTotalAttachments = TotalArtifactSizeInBytes }; testRunShard.UploadCompleted = true; return testRunShard; } diff --git a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs index 5d7e3f4fc7af..d17aa019b488 100644 --- a/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs +++ b/sdk/playwrighttesting/Azure.Developer.MicrosoftPlaywrightTesting.TestLogger/src/Utility/ReporterUtils.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; @@ -114,6 +115,15 @@ internal static string GetCurrentOS() else return OSConstants.s_wINDOWS; } + internal static string? GetCloudFileName(string filePath, string testExecutionId) + { + var fileName = Path.GetFileName(filePath); + if (fileName == null) + { + return null; + } + return $"{testExecutionId}/{fileName}"; // TODO check if we need to add {Guid.NewGuid()} for file with same name + } internal TokenDetails ParseWorkspaceIdFromAccessToken(JsonWebTokenHandler? jsonWebTokenHandler, string? accessToken) {