Skip to content

Commit 78a9ba2

Browse files
authored
Set correct target framework for long running test (#3474)
* Set correct target framework * Split long and short haul tests but maintain most of the current code
1 parent 773af55 commit 78a9ba2

File tree

2 files changed

+84
-91
lines changed

2 files changed

+84
-91
lines changed

.github/workflows/stability-test.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ jobs:
1717
name: Connection Stability Test
1818
runs-on: ubuntu-latest
1919
timeout-minutes: 120 # Allow extra time beyond test duration for setup/teardown
20-
20+
2121
permissions:
2222
contents: read
2323

2424
env:
2525
DOTNET_VERSION: '10.0.x'
26+
TARGET_FRAMEWORK: 'net10.0'
2627
CONFIGURATION: 'Release'
2728
TEST_DURATION_MINUTES: ${{ github.event.inputs.duration || '90' }}
2829

@@ -53,7 +54,7 @@ jobs:
5354
dotnet test ./Tests/Opc.Ua.Client.Tests/Opc.Ua.Client.Tests.csproj \
5455
--configuration ${{ env.CONFIGURATION }} \
5556
--no-build \
56-
--framework ${{ env.DOTNET_VERSION }}
57+
--framework ${{ env.TARGET_FRAMEWORK }} \
5758
--filter "Category=ConnectionStability" \
5859
--logger "console;verbosity=detailed" \
5960
--results-directory ./TestResults

Tests/Opc.Ua.Client.Tests/ConnectionStabilityTest.cs

Lines changed: 81 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -43,94 +43,99 @@ namespace Opc.Ua.Client.Tests
4343
/// Long-running connection stability test.
4444
/// </summary>
4545
[TestFixture]
46-
[Category("ConnectionStability")]
4746
[SetCulture("en-us")]
4847
[SetUICulture("en-us")]
48+
[Category("Client")]
4949
public class ConnectionStabilityTest : ClientTestFramework
5050
{
51-
private const int SecurityTokenLifetimeCIMs = 5 * 60 * 1000; // 5 minutes for CI
52-
private const int SecurityTokenLifetimeLocalMs = 10 * 1000; // 10 seconds for local testing
53-
private const int StatusReportIntervalSeconds = 60; // Report status every 60 seconds
54-
private const double NotificationToleranceRatio = 0.95; // Accept 95% of expected notifications (5% tolerance)
55-
56-
public ConnectionStabilityTest()
57-
: base(Utils.UriSchemeOpcTcp)
58-
{
59-
SingleSession = false;
60-
}
61-
6251
/// <summary>
63-
/// Set up a Server and a Client instance.
52+
/// 5 minutes for CI
6453
/// </summary>
65-
[OneTimeSetUp]
66-
public override async Task OneTimeSetUpAsync()
67-
{
68-
SupportsExternalServerUrl = true;
69-
70-
// Check if running in CI environment
71-
bool isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI")) ||
72-
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"));
54+
private const int kSecurityTokenLifetimeCIMs = 5 * 60 * 1000;
7355

74-
// Configure security token lifetime based on environment
75-
// CI: 5 minutes to force 18 renewals in 90 minute test
76-
// Local: 10 seconds to force 6 renewals in 1 minute test
77-
int tokenLifetime = isCI ? SecurityTokenLifetimeCIMs : SecurityTokenLifetimeLocalMs;
78-
79-
SecurityTokenLifetime = tokenLifetime;
56+
/// <summary>
57+
/// 10 seconds for local testing
58+
/// </summary>
59+
private const int kSecurityTokenLifetimeLocalMs = 10 * 1000;
8060

81-
await base.OneTimeSetUpAsync().ConfigureAwait(false);
82-
}
61+
/// <summary>
62+
/// Report status every 60 seconds
63+
/// </summary>
64+
private const int kStatusReportIntervalSeconds = 60;
8365

8466
/// <summary>
85-
/// Tear down the Server and the Client.
67+
/// Accept 95% of expected notifications (5% tolerance)
8668
/// </summary>
87-
[OneTimeTearDown]
88-
public override Task OneTimeTearDownAsync()
69+
private const double kNotificationToleranceRatio = 0.95;
70+
71+
public ConnectionStabilityTest()
72+
: base(Utils.UriSchemeOpcTcp)
8973
{
90-
return base.OneTimeTearDownAsync();
74+
SupportsExternalServerUrl = true;
9175
}
9276

93-
/// <summary>
94-
/// Test setup.
95-
/// </summary>
96-
[SetUp]
97-
public override Task SetUpAsync()
77+
[Test]
78+
[Order(100)]
79+
public async Task ShortHaulStabilityTestAsync()
9880
{
99-
return base.SetUpAsync();
81+
try
82+
{
83+
SecurityTokenLifetime = kSecurityTokenLifetimeLocalMs;
84+
await OneTimeSetUpAsync().ConfigureAwait(false);
85+
86+
// 2 minutes for local testing
87+
await RunStabilityTestAsync(2).ConfigureAwait(false);
88+
}
89+
finally
90+
{
91+
await OneTimeTearDownAsync().ConfigureAwait(false);
92+
}
10093
}
10194

102-
/// <summary>
103-
/// Test teardown.
104-
/// </summary>
105-
[TearDown]
106-
public override Task TearDownAsync()
95+
[Test]
96+
[Order(100)]
97+
[Explicit]
98+
[Category("ConnectionStability")]
99+
public async Task LongHaulStabilityTestAsync()
107100
{
108-
return base.TearDownAsync();
101+
try
102+
{
103+
SecurityTokenLifetime = kSecurityTokenLifetimeCIMs;
104+
await OneTimeSetUpAsync().ConfigureAwait(false);
105+
106+
// Configurable duration for CI testing
107+
string envValue = Environment.GetEnvironmentVariable("TEST_DURATION_MINUTES");
108+
if (string.IsNullOrEmpty(envValue) ||
109+
!int.TryParse(envValue, out int minutes) ||
110+
minutes <= 0)
111+
{
112+
minutes = 90; // Default to 90 minutes for CI
113+
}
114+
await RunStabilityTestAsync(minutes).ConfigureAwait(false);
115+
}
116+
finally
117+
{
118+
await OneTimeTearDownAsync().ConfigureAwait(false);
119+
}
109120
}
110121

111122
/// <summary>
112123
/// Long-running test that verifies connection stability over a configurable duration.
113124
/// Tests that:
114125
/// - Connection remains stable over extended period
115126
/// - Subscriptions deliver all expected values (no message loss)
116-
/// - Security token renewals happen correctly (every 5 minutes in CI, every 10 seconds locally)
117-
/// Duration can be configured via TEST_DURATION_MINUTES environment variable (default: 90 minutes CI, 1 minute local)
127+
/// - Security token renewals happen correctly
118128
/// </summary>
119-
[Test]
120-
[Order(100)]
121-
public async Task LongRunningStabilityTestAsync()
129+
private async Task RunStabilityTestAsync(int testDurationMinutes)
122130
{
123131
// Get test duration from environment variable or use default
124-
int testDurationMinutes = GetTestDurationMinutes();
125132
int testDurationSeconds = testDurationMinutes * 60;
133+
int tokenLifetimeMs = SecurityTokenLifetime;
126134

127-
// Determine token lifetime based on environment
128-
bool isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI")) ||
129-
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"));
130-
int tokenLifetimeMs = isCI ? SecurityTokenLifetimeCIMs : SecurityTokenLifetimeLocalMs;
131-
132-
TestContext.Out.WriteLine($"Starting connection stability test for {testDurationMinutes} minutes ({testDurationSeconds} seconds)");
133-
TestContext.Out.WriteLine($"Security token lifetime: {tokenLifetimeMs / 1000} seconds ({tokenLifetimeMs / 60000.0:F1} minutes)");
135+
TestContext.Out.WriteLine(
136+
$"Starting connection stability test for {testDurationMinutes} minutes ({testDurationSeconds} seconds)");
137+
TestContext.Out.WriteLine(
138+
$"Security token lifetime: {tokenLifetimeMs / 1000} seconds ({tokenLifetimeMs / 60000.0:F1} minutes)");
134139

135140
const int publishingInterval = 1000; // 1 second
136141
const int writerInterval = 2000; // 2 seconds
@@ -155,7 +160,9 @@ public async Task LongRunningStabilityTestAsync()
155160
TestContext.Out.WriteLine($"Subscribing to {nodeIds.Count} nodes.");
156161

157162
// Create session
158-
session = await ClientFixture.ConnectAsync(ServerUrl, SecurityPolicies.Basic256Sha256).ConfigureAwait(false);
163+
session = await ClientFixture.ConnectAsync(
164+
ServerUrl,
165+
SecurityPolicies.Basic256Sha256).ConfigureAwait(false);
159166
Assert.NotNull(session, "Failed to create session");
160167

161168
// Create subscription
@@ -221,7 +228,9 @@ public async Task LongRunningStabilityTestAsync()
221228
TestContext.Out.WriteLine($"Subscription created with {subscription.MonitoredItemCount} monitored items");
222229

223230
// Create writer session
224-
ISession writerSession = await ClientFixture.ConnectAsync(ServerUrl, SecurityPolicies.Basic256Sha256).ConfigureAwait(false);
231+
ISession writerSession = await ClientFixture.ConnectAsync(
232+
ServerUrl,
233+
SecurityPolicies.Basic256Sha256).ConfigureAwait(false);
225234
Assert.NotNull(writerSession, "Failed to create writer session");
226235

227236
// Writer task - continuously write values
@@ -282,7 +291,9 @@ public async Task LongRunningStabilityTestAsync()
282291
{
283292
try
284293
{
285-
await Task.Delay(TimeSpan.FromSeconds(StatusReportIntervalSeconds), statusReportingCts.Token).ConfigureAwait(false);
294+
await Task.Delay(
295+
TimeSpan.FromSeconds(kStatusReportIntervalSeconds),
296+
statusReportingCts.Token).ConfigureAwait(false);
286297
}
287298
catch (OperationCanceledException)
288299
{
@@ -291,7 +302,7 @@ public async Task LongRunningStabilityTestAsync()
291302

292303
reportCount++;
293304
int totalNotifications = valueChanges.Values.Sum();
294-
int elapsedMinutes = reportCount * StatusReportIntervalSeconds / 60;
305+
int elapsedMinutes = reportCount * kStatusReportIntervalSeconds / 60;
295306

296307
TestContext.Out.WriteLine(
297308
$"[Status Report {reportCount}] Elapsed: {elapsedMinutes} minutes, " +
@@ -302,7 +313,7 @@ public async Task LongRunningStabilityTestAsync()
302313
if (reportCount % 5 == 0) // Every 5 minutes
303314
{
304315
TestContext.Out.WriteLine("Per-node notification counts:");
305-
foreach (var kvp in valueChanges.OrderBy(x => x.Key.ToString()))
316+
foreach (KeyValuePair<NodeId, int> kvp in valueChanges.OrderBy(x => x.Key.ToString()))
306317
{
307318
TestContext.Out.WriteLine($" {kvp.Key}: {kvp.Value} notifications");
308319
}
@@ -346,7 +357,7 @@ public async Task LongRunningStabilityTestAsync()
346357
TestContext.Out.WriteLine("=== Final Results ===");
347358
TestContext.Out.WriteLine($"Test duration: {testDurationMinutes} minutes");
348359
TestContext.Out.WriteLine($"Security token lifetime: {tokenLifetimeMs / 1000} seconds ({tokenLifetimeMs / 60000.0:F1} minutes)");
349-
TestContext.Out.WriteLine($"Expected token renewals: ~{(testDurationMinutes * 60000) / tokenLifetimeMs} times");
360+
TestContext.Out.WriteLine($"Expected token renewals: ~{testDurationMinutes * 60000 / tokenLifetimeMs} times");
350361
TestContext.Out.WriteLine($"Total write operations: {writeCount}");
351362
TestContext.Out.WriteLine($"Total errors: {errors.Count}");
352363

@@ -367,10 +378,10 @@ public async Task LongRunningStabilityTestAsync()
367378
#if DEBUG
368379
TestContext.Out.WriteLine($" {nodeId}: {changes} notifications");
369380
#endif
370-
if (changes < (writeCount * NotificationToleranceRatio))
381+
if (changes < (writeCount * kNotificationToleranceRatio))
371382
{
372383
allNodesReceivedData = false;
373-
TestContext.Out.WriteLine($" WARNING: Expected at least {writeCount * NotificationToleranceRatio:F0} notifications");
384+
TestContext.Out.WriteLine($" WARNING: Expected at least {writeCount * kNotificationToleranceRatio:F0} notifications");
374385
}
375386
}
376387
else
@@ -408,7 +419,10 @@ public async Task LongRunningStabilityTestAsync()
408419
// Assertions
409420
Assert.IsTrue(allNodesReceivedData, "Not all nodes received expected data");
410421
Assert.AreEqual(0, errors.Count, $"Test encountered {errors.Count} errors");
411-
Assert.GreaterOrEqual(totalNotifications, expectedMinNotifications, "Total notifications received is less than expected minimum");
422+
Assert.GreaterOrEqual(
423+
totalNotifications,
424+
expectedMinNotifications,
425+
"Total notifications received is less than expected minimum");
412426

413427
TestContext.Out.WriteLine("Connection stability test PASSED");
414428
}
@@ -441,27 +455,5 @@ public async Task LongRunningStabilityTestAsync()
441455
}
442456
}
443457
}
444-
445-
/// <summary>
446-
/// Gets the test duration in minutes from environment variable or returns default.
447-
/// </summary>
448-
private int GetTestDurationMinutes()
449-
{
450-
string envValue = Environment.GetEnvironmentVariable("TEST_DURATION_MINUTES");
451-
452-
if (!string.IsNullOrEmpty(envValue) && int.TryParse(envValue, out int minutes) && minutes > 0)
453-
{
454-
return minutes;
455-
}
456-
457-
// Default to 90 minutes for nightly runs, but use 1 minute for manual/local testing
458-
// CI: 90 minutes with 5-minute token lifetime = 18 renewals
459-
// Local: 1 minute with 10-second token lifetime = 6 renewals
460-
// Check if running in CI environment
461-
bool isCI = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CI")) ||
462-
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS"));
463-
464-
return isCI ? 90 : 1; // 90 minutes for CI (18 renewals), 1 minute for local (6 renewals)
465-
}
466458
}
467459
}

0 commit comments

Comments
 (0)