Skip to content

Commit

Permalink
Launch artifacts extension (#148)
Browse files Browse the repository at this point in the history
* Simple artifacts collecting

* Docs

* Update Configuration.md

* Update Configuration.md
  • Loading branch information
nvborisenko authored Oct 11, 2024
1 parent 8819de4 commit a42f9f3
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 3 deletions.
15 changes: 13 additions & 2 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,24 @@ Requests are not repeated, only 1 attempt is allocated for all requests.
# Reporting Experience

If you want to redirect agent to send test results into some another way, there are several options:
- `Launch:Id` (UUID of existing launch) - agent will append test results into provided Launch ID. Launch should be *IN_PROGRESS* state, agent will not finish it. It's your responsibility to start and finish launch. Usefull for distributed test execution, where tests are running on different machines and you want to see consolidated report.
- `Launch:Id` (UUID of existing launch) - agent will append test results into provided Launch ID. Launch should be *IN_PROGRESS* state, agent will not finish it. It's your responsibility to start and finish launch. Useful for distributed test execution, where tests are running on different machines and you want to see consolidated report.
- `Launch:Rerun` (true/false/yes/no) - agent will try to add new tests into existing launch (compared by name) or adds new attempt/retry for existing tests.
- `Launch:RerunOf` (UUID of existing launch) - agent will try to add new tests into existing launch (by ID) or adds new attempt/retry for existing tests. Takes effect only if `Launch:Rerun` is `true`.

## Attachments
In additional of attaching artifacts during tests execution [dynamically](./Logging.md), it is possible to configure file attachments at launch level statically via files pattern. Set `Launch:Artifacts` configuration property to set of file patterns, and files will be automatically attached after tests execution.

Example:
```json
{
"launch": {
"artifacts": [ "*.log", "screenshots/*.png" ]
}
}
```

# Analytics

Each time when new launch is posted to RP server, reporting engine sends this fact to google analytics service. It doesn't collect sensetive information, just name and version of used engine/agent.
Each time when new launch is posted to RP server, reporting engine sends this fact to google analytics service. It doesn't collect sensitive information, just name and version of used engine/agent.

This behavior can be turned off through `Analytics:Enabled` configuration property.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using ReportPortal.Client.Abstractions.Requests;
using ReportPortal.Shared.Extensibility.ReportEvents;
using ReportPortal.Shared.Internal.Logging;
using ReportPortal.Shared.MimeTypes;
using System;
using System.IO;
using System.Threading.Tasks;

namespace ReportPortal.Shared.Extensibility.Embedded.LaunchArtifacts
{
public class LaunchArtifactsEventsObserver : IReportEventsObserver
{
private static readonly ITraceLogger _logger = TraceLogManager.Instance.GetLogger(typeof(LaunchArtifactsEventsObserver));

public string BaseDirectory { get; set; } = Environment.CurrentDirectory;

public void Initialize(IReportEventsSource reportEventsSource)
{
reportEventsSource.OnBeforeLaunchFinishing += ReportEventsSource_OnBeforeLaunchFinishing;
}

private void ReportEventsSource_OnBeforeLaunchFinishing(Reporter.ILaunchReporter launchReporter, ReportEvents.EventArgs.BeforeLaunchFinishingEventArgs args)
{
var artifactPaths = args.Configuration.GetValues<string>("Launch:Artifacts", null);

if (artifactPaths != null)
{
foreach (var filePattern in artifactPaths)
{
var artifacts = Directory.GetFiles(BaseDirectory, filePattern);

foreach (var artifact in artifacts)
{
var createLogItemRequest = new CreateLogItemRequest
{
LaunchUuid = launchReporter.Info.Uuid,
Time = DateTime.UtcNow,
Level = Client.Abstractions.Models.LogLevel.Trace,
Text = Path.GetFileName(artifact),
};

AttachFile(artifact, ref createLogItemRequest);

Task.Run(async () => await args.ClientService.LogItem.CreateAsync(createLogItemRequest)).GetAwaiter().GetResult();
}
}
}
}

private static void AttachFile(string filePath, ref CreateLogItemRequest request)
{
try
{
using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var memoryStream = new MemoryStream())
{
fileStream.CopyTo(memoryStream);
var bytes = memoryStream.ToArray();

request.Attach = new LogItemAttach
{
Name = Path.GetFileName(filePath),
MimeType = MimeTypeMap.GetMimeType(Path.GetExtension(filePath)),
Data = bytes
};
}
}
}
catch (Exception ex)
{
request.Text = $"{request.Text}\n> Couldn't read content of `{filePath}` file. \n{ex}";
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using FluentAssertions;
using Moq;
using ReportPortal.Client.Abstractions.Requests;
using ReportPortal.Shared.Configuration;
using ReportPortal.Shared.Extensibility;
using ReportPortal.Shared.Extensibility.Embedded.LaunchArtifacts;
using ReportPortal.Shared.Tests.Helpers;
using System.IO;
using System.Threading;
using Xunit;

namespace ReportPortal.Shared.Tests.Extensibility.Embedded.LaunchArtifacts
{
public class LaunchArtifactsTest
{
private readonly IExtensionManager _extensionManager;

public LaunchArtifactsTest()
{
_extensionManager = new Shared.Extensibility.ExtensionManager();
_extensionManager.ReportEventObservers.Add(new LaunchArtifactsEventsObserver());
}

[Fact]
public void ShouldNotAttachArtifacts()
{
var client = new MockServiceBuilder().Build();

var launchReporter = new LaunchReporterBuilder(client.Object).With(_extensionManager).Build(1, 0, 0);
launchReporter.Sync();

client.Verify(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest[]>(), default), Times.Never());
}

[Fact]
public void ShouldAttachSingleArtifactByPath()
{
File.WriteAllBytes("test_file_1.txt", new byte[] { 1, 2, 3 });

CreateLogItemRequest request = null;

var client = new MockServiceBuilder().Build();
client.Setup(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default))
.Callback<CreateLogItemRequest, CancellationToken>((a, b) => request = a);

var config = new ConfigurationBuilder().Build();
config.Properties["launch:artifacts"] = "test_file_1.txt";

var launchReporter = new LaunchReporterBuilder(client.Object).With(_extensionManager).WithConfiguration(config).Build(1, 0, 0);
launchReporter.Sync();

client.Verify(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default), Times.Once);

request.Should().NotBeNull();
request.Text.Should().Be("test_file_1.txt");

request.Attach.Should().NotBeNull();
request.Attach.Name.Should().Be("test_file_1.txt");
request.Attach.MimeType.Should().Be("text/plain");
request.Attach.Data.Should().BeEquivalentTo(new byte[] { 1, 2, 3 });
}

[Fact]
public void ShouldAttachSeveralArtifactsByPath()
{
File.Create("test_file_1.txt").Close();
File.Create("test_file_2.txt").Close();

var client = new MockServiceBuilder().Build();

var config = new ConfigurationBuilder().Build();
config.Properties["launch:artifacts"] = "test_file_1.txt;test_file_2.txt";

var launchReporter = new LaunchReporterBuilder(client.Object).With(_extensionManager).WithConfiguration(config).Build(1, 0, 0);
launchReporter.Sync();

client.Verify(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default), Times.Exactly(2));
}

[Fact]
public void ShouldAttachSingleArtifactByPattern()
{
File.WriteAllBytes("test_file_1.txt", new byte[] { 1, 2, 3 });

CreateLogItemRequest request = null;

var client = new MockServiceBuilder().Build();
client.Setup(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default))
.Callback<CreateLogItemRequest, CancellationToken>((a, b) => request = a);

var config = new ConfigurationBuilder().Build();
config.Properties["launch:artifacts"] = "test_*_1.txt";

var launchReporter = new LaunchReporterBuilder(client.Object).With(_extensionManager).WithConfiguration(config).Build(1, 0, 0);
launchReporter.Sync();

client.Verify(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default), Times.Once);
}

[Fact]
public void ShouldAttachSingleArtifactByDir()
{
Directory.CreateDirectory("a/b/c");
File.WriteAllBytes("a/b/c/abc.txt", new byte[] { 1, 2, 3 });

CreateLogItemRequest request = null;

var client = new MockServiceBuilder().Build();
client.Setup(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default))
.Callback<CreateLogItemRequest, CancellationToken>((a, b) => request = a);

var config = new ConfigurationBuilder().Build();
config.Properties["launch:artifacts"] = "a/b/c/*.txt";

var launchReporter = new LaunchReporterBuilder(client.Object).With(_extensionManager).WithConfiguration(config).Build(1, 0, 0);
launchReporter.Sync();

client.Verify(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default), Times.Once);
}

[Fact]
public void ShouldAttachMessageWithException()
{
File.Create("test_file_open.txt"); // leaves it open

CreateLogItemRequest request = null;

var client = new MockServiceBuilder().Build();
client.Setup(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default))
.Callback<CreateLogItemRequest, CancellationToken>((a, b) => request = a);

var config = new ConfigurationBuilder().Build();
config.Properties["launch:artifacts"] = "test_file_open.txt";

var launchReporter = new LaunchReporterBuilder(client.Object).With(_extensionManager).WithConfiguration(config).Build(1, 0, 0);
launchReporter.Sync();

client.Verify(s => s.LogItem.CreateAsync(It.IsAny<CreateLogItemRequest>(), default), Times.Once);

request.Text.Should().Contain("Couldn't read");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public void ShouldExploreExtensions()
manager.Explore(Environment.CurrentDirectory);

manager.ReportEventObservers.Count.Should()
.Be(2, "default and google analytic observers should be registered by default");
.Be(3, "normalizer, google analytic, launch artifacts - observers should be registered by default");

manager.CommandsListeners.Should().HaveCount(1);
}
Expand Down

0 comments on commit a42f9f3

Please sign in to comment.