Skip to content

Commit 6bb7fc9

Browse files
authored
[Profiler] Add Flaky attribute (#8648)
1 parent 71790f0 commit 6bb7fc9

4 files changed

Lines changed: 121 additions & 2 deletions

File tree

profiler/test/Datadog.Profiler.IntegrationTests/Exceptions/ExceptionsTest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Linq;
1010
using System.Text;
1111
using Datadog.Profiler.IntegrationTests.Helpers;
12+
using Datadog.Profiler.IntegrationTests.Xunit;
1213
using FluentAssertions;
1314
using Xunit;
1415
using Xunit.Abstractions;
@@ -29,6 +30,7 @@ public ExceptionsTest(ITestOutputHelper output)
2930
_output = output;
3031
}
3132

33+
[Flaky("Flaky on ARM64")]
3234
[TestAppFact("Samples.ExceptionGenerator")]
3335
public void ThrowExceptionsInParallel(string appName, string framework, string appAssembly)
3436
{
@@ -145,6 +147,7 @@ public void ThrowExceptionsInParallel(string appName, string framework, string a
145147
}
146148
}
147149

150+
[Flaky("Flaky on ARM64")]
148151
[TestAppFact("Samples.ExceptionGenerator")]
149152
public void ThrowExceptionsInParallelWithCustomGetFunctionFromIp(string appName, string framework, string appAssembly)
150153
{
@@ -262,6 +265,7 @@ public void ThrowExceptionsInParallelWithCustomGetFunctionFromIp(string appName,
262265
}
263266
}
264267

268+
[Flaky("Flaky on ARM64")]
265269
[Trait("Category", "LinuxOnly")]
266270
[TestAppFact("Samples.ExceptionGenerator", new[] { "net48", "netcoreapp3.1", "net6.0", "net8.0", })] // FIXME: .NET 9 skipping .NET 9 for now
267271
public void ThrowExceptionsInParallelWithNewCpuProfiler(string appName, string framework, string appAssembly)
@@ -374,6 +378,7 @@ public void Sampling(string appName, string framework, string appAssembly)
374378
exceptionCounts.Should().ContainKey("System.InvalidOperationException").WhoseValue.Should().Be(1);
375379
}
376380

381+
[Flaky("Flaky on ARM64")]
377382
[TestAppFact("Samples.ExceptionGenerator", new[] { "net48", "netcoreapp3.1", "net6.0", "net8.0", })] // FIXME: .NET 9 skipping .NET 9 for now
378383
public void GetExceptionSamplesWithTimestamp(string appName, string framework, string appAssembly)
379384
{
@@ -385,6 +390,7 @@ public void GetExceptionSamplesWithTimestamp(string appName, string framework, s
385390
CheckExceptionProfiles(runner, true);
386391
}
387392

393+
[Flaky("Flaky on ARM64")]
388394
[TestAppFact("Samples.ExceptionGenerator", new[] { "net48", "netcoreapp3.1", "net6.0", "net8.0", })] // FIXME: .NET 9 skipping .NET 9 for now
389395
public void GetExceptionSamplesWithoutTimestamp(string appName, string framework, string appAssembly)
390396
{
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// <copyright file="DelayedMessageBus.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
using System.Collections.Concurrent;
7+
using Xunit.Abstractions;
8+
using Xunit.Sdk;
9+
10+
namespace Datadog.Profiler.IntegrationTests.Xunit;
11+
12+
/// <summary>
13+
/// Queues messages instead of forwarding them immediately.
14+
/// On <see cref="Dispose"/>, all queued messages are flushed to the inner bus.
15+
/// Used by <see cref="ProfilerTestCase"/> to discard intermediate failure messages on retry.
16+
/// Based on https://github.com/xunit/samples.xunit/blob/main/v2/RetryFactExample/DelayedMessageBus.cs
17+
/// </summary>
18+
internal class DelayedMessageBus : IMessageBus
19+
{
20+
private readonly IMessageBus _innerBus;
21+
private readonly ConcurrentQueue<IMessageSinkMessage> _messages = new();
22+
23+
public DelayedMessageBus(IMessageBus innerBus)
24+
{
25+
_innerBus = innerBus;
26+
}
27+
28+
public bool QueueMessage(IMessageSinkMessage message)
29+
{
30+
_messages.Enqueue(message);
31+
return true;
32+
}
33+
34+
public void Dispose()
35+
{
36+
while (_messages.TryDequeue(out var message))
37+
{
38+
_innerBus.QueueMessage(message);
39+
}
40+
}
41+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// <copyright file="FlakyAttribute.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2022 Datadog, Inc.
4+
// </copyright>
5+
6+
using System;
7+
8+
namespace Datadog.Profiler.IntegrationTests.Xunit;
9+
10+
/// <summary>
11+
/// Marks a test as flaky, so that it is automatically retried by <see cref="ProfilerTestCase"/>.
12+
/// </summary>
13+
/// <param name="reason">The reason that this test was marked flaky.</param>
14+
/// <param name="maxRetries">The maximum number of times a test should be retried (default 5).</param>
15+
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
16+
public class FlakyAttribute(string reason, byte maxRetries = 5) : Attribute
17+
{
18+
public string Reason { get; } = reason;
19+
20+
public byte MaxRetries { get; } = maxRetries;
21+
}

profiler/test/Datadog.Profiler.IntegrationTests/Xunit/ProfilerTestCase.cs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// </copyright>
55

66
using System;
7+
using System.Reflection;
78
using System.Threading;
89
using System.Threading.Tasks;
910
using Xunit.Abstractions;
@@ -29,12 +30,62 @@ public ProfilerTestCase(
2930
{
3031
}
3132

32-
public override Task<RunSummary> RunAsync(
33+
public override async Task<RunSummary> RunAsync(
3334
IMessageSink diagnosticMessageSink,
3435
IMessageBus messageBus,
3536
object[] constructorArguments,
3637
ExceptionAggregator aggregator,
3738
CancellationTokenSource cancellationTokenSource)
38-
=> new ProfilerTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource).RunAsync();
39+
{
40+
int attemptsRemaining = 1;
41+
var retryReason = string.Empty;
42+
43+
try
44+
{
45+
var flakyAttribute = TestMethod.Method.ToRuntimeMethod().GetCustomAttribute<FlakyAttribute>();
46+
if (flakyAttribute is not null)
47+
{
48+
// First attempt + retries
49+
attemptsRemaining = flakyAttribute.MaxRetries + 1;
50+
retryReason = flakyAttribute.Reason;
51+
}
52+
}
53+
catch (Exception e)
54+
{
55+
diagnosticMessageSink.OnMessage(new DiagnosticMessage($"ERROR: Looking for FlakyAttribute: {e}"));
56+
}
57+
58+
if (attemptsRemaining <= 1)
59+
{
60+
return await RunOnceAsync(messageBus);
61+
}
62+
63+
DelayedMessageBus delayedBus = null;
64+
try
65+
{
66+
while (true)
67+
{
68+
attemptsRemaining--;
69+
delayedBus = new DelayedMessageBus(messageBus);
70+
71+
var summary = await RunOnceAsync(delayedBus);
72+
73+
if (summary.Failed == 0 || attemptsRemaining <= 0)
74+
{
75+
return summary;
76+
}
77+
78+
diagnosticMessageSink.OnMessage(
79+
new DiagnosticMessage($"RETRYING: {DisplayName} ({attemptsRemaining} attempts remaining, {retryReason})"));
80+
}
81+
}
82+
finally
83+
{
84+
delayedBus?.Dispose();
85+
}
86+
87+
Task<RunSummary> RunOnceAsync(IMessageBus bus)
88+
=> new ProfilerTestCaseRunner(this, DisplayName, SkipReason, constructorArguments, TestMethodArguments, bus, aggregator, cancellationTokenSource).RunAsync();
89+
}
3990
}
4091
}

0 commit comments

Comments
 (0)