Skip to content

Commit 1ac371b

Browse files
authored
Merge pull request #962 from softworkz/submit_testupd
Collection of test fixes
2 parents 2c82206 + 95e02e2 commit 1ac371b

31 files changed

+652
-429
lines changed

src/ElectronNET.API/API/ApiBase.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ protected enum SocketEventNameTypes
3131
CamelCase,
3232
}
3333

34-
private const int InvocationTimeout = 1000;
34+
private static readonly TimeSpan InvocationTimeout = 1000.ms();
3535

3636
private readonly string objectName;
3737
private readonly ConcurrentDictionary<string, Invocator> invocators;
@@ -120,7 +120,7 @@ protected Task<T> InvokeAsync<T>(object arg = null, [CallerMemberName] string ca
120120
return this.InvokeAsyncWithTimeout<T>(InvocationTimeout, arg, callerName);
121121
}
122122

123-
protected Task<T> InvokeAsyncWithTimeout<T>(int invocationTimeout, object arg = null, [CallerMemberName] string callerName = null)
123+
protected Task<T> InvokeAsyncWithTimeout<T>(TimeSpan invocationTimeout, object arg = null, [CallerMemberName] string callerName = null)
124124
{
125125
Debug.Assert(callerName != null, nameof(callerName) + " != null");
126126

@@ -245,7 +245,7 @@ internal class Invocator<T> : Invocator
245245
private readonly Task<T> tcsTask;
246246
private TaskCompletionSource<T> tcs;
247247

248-
public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg = null)
248+
public Invocator(ApiBase apiBase, string callerName, TimeSpan timeout, object arg = null)
249249
{
250250
this.tcs = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);
251251
this.tcsTask = this.tcs.Task;
@@ -306,15 +306,15 @@ public Invocator(ApiBase apiBase, string callerName, int timeoutMs, object arg =
306306
_ = apiBase.Id >= 0 ? BridgeConnector.Socket.Emit(messageName, apiBase.Id) : BridgeConnector.Socket.Emit(messageName);
307307
}
308308

309-
System.Threading.Tasks.Task.Delay(timeoutMs).ContinueWith(_ =>
309+
System.Threading.Tasks.Task.Delay(timeout).ContinueWith(_ =>
310310
{
311311
if (this.tcs != null)
312312
{
313313
lock (this)
314314
{
315315
if (this.tcs != null)
316316
{
317-
var ex = new TimeoutException($"No response after {timeoutMs:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
317+
var ex = new TimeoutException($"No response after {timeout:D}ms trying to retrieve value {apiBase.objectName}.{callerName}()");
318318
this.tcs.TrySetException(ex);
319319
this.tcs = null;
320320
}

src/ElectronNET.API/API/WebContents.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
using ElectronNET.API.Entities;
21
using System;
32
using System.Threading.Tasks;
3+
using ElectronNET.API.Entities;
4+
using ElectronNET.Common;
45

56
// ReSharper disable InconsistentNaming
67

@@ -173,7 +174,7 @@ public bool IsDevToolsFocused()
173174
/// Get system printers.
174175
/// </summary>
175176
/// <returns>printers</returns>
176-
public Task<PrinterInfo[]> GetPrintersAsync() => this.InvokeAsyncWithTimeout<PrinterInfo[]>(5_000);
177+
public Task<PrinterInfo[]> GetPrintersAsync() => this.InvokeAsyncWithTimeout<PrinterInfo[]>(8.seconds());
177178

178179
/// <summary>
179180
/// Prints window's web page.
@@ -388,7 +389,7 @@ public void SetAudioMuted(bool muted)
388389
/// Returns string - The user agent for this web page.
389390
/// </summary>
390391
/// <returns></returns>
391-
public Task<string> GetUserAgentAsync() => InvokeAsync<string>();
392+
public Task<string> GetUserAgentAsync() => InvokeAsyncWithTimeout<string>(3.seconds());
392393

393394
/// <summary>
394395
/// Overrides the user agent for this web page.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// <copyright file="TimeSpanExtensions.cs" company="Emby LLC">
2+
// Copyright © Emby LLC. All rights reserved.
3+
// </copyright>
4+
5+
namespace ElectronNET.Common
6+
{
7+
using System;
8+
using System.Diagnostics.CodeAnalysis;
9+
10+
/// <summary>
11+
/// The TimeSpanExtensions class.
12+
/// </summary>
13+
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "OK")]
14+
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "OK")]
15+
[SuppressMessage("ReSharper", "StyleCop.SA1300", Justification = "OK")]
16+
[SuppressMessage("ReSharper", "InconsistentNaming", Justification = "OK")]
17+
internal static class TimeSpanExtensions
18+
{
19+
public static TimeSpan ms(this int value)
20+
{
21+
return TimeSpan.FromMilliseconds(value);
22+
}
23+
24+
public static TimeSpan ms(this long value)
25+
{
26+
return TimeSpan.FromMilliseconds(value);
27+
}
28+
29+
public static TimeSpan seconds(this int value)
30+
{
31+
return TimeSpan.FromSeconds(value);
32+
}
33+
34+
public static TimeSpan minutes(this int value)
35+
{
36+
return TimeSpan.FromMinutes(value);
37+
}
38+
39+
public static TimeSpan hours(this int value)
40+
{
41+
return TimeSpan.FromHours(value);
42+
}
43+
44+
public static TimeSpan days(this int value)
45+
{
46+
return TimeSpan.FromDays(value);
47+
}
48+
49+
public static TimeSpan ms(this double value)
50+
{
51+
return TimeSpan.FromMilliseconds(value);
52+
}
53+
54+
public static TimeSpan seconds(this double value)
55+
{
56+
return TimeSpan.FromSeconds(value);
57+
}
58+
59+
public static TimeSpan minutes(this double value)
60+
{
61+
return TimeSpan.FromMinutes(value);
62+
}
63+
64+
public static TimeSpan hours(this double value)
65+
{
66+
return TimeSpan.FromHours(value);
67+
}
68+
69+
public static TimeSpan days(this double value)
70+
{
71+
return TimeSpan.FromDays(value);
72+
}
73+
}
74+
}

src/ElectronNET.API/ElectronNET.API.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,6 @@
3535
<ItemGroup>
3636
<InternalsVisibleTo Include="ElectronNET.AspNet" />
3737
<InternalsVisibleTo Include="ElectronNET.Core.AspNet" />
38+
<InternalsVisibleTo Include="ElectronNET.IntegrationTests" />
3839
</ItemGroup>
3940
</Project>

src/ElectronNET.API/Runtime/Services/ElectronProcess/ElectronProcessActive.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private async Task StartInternal(string startCmd, string args, string directoriy
7676
{
7777
try
7878
{
79-
await Task.Delay(10).ConfigureAwait(false);
79+
await Task.Delay(10.ms()).ConfigureAwait(false);
8080

8181
Console.Error.WriteLine("[StartInternal]: startCmd: {0}", startCmd);
8282
Console.Error.WriteLine("[StartInternal]: args: {0}", args);
@@ -85,7 +85,7 @@ private async Task StartInternal(string startCmd, string args, string directoriy
8585
this.process.ProcessExited += this.Process_Exited;
8686
this.process.Run(startCmd, args, directoriy);
8787

88-
await Task.Delay(500).ConfigureAwait(false);
88+
await Task.Delay(500.ms()).ConfigureAwait(false);
8989

9090
Console.Error.WriteLine("[StartInternal]: after run:");
9191

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
namespace ElectronNET.IntegrationTests.Common
2+
{
3+
using System.Runtime.InteropServices;
4+
using Xunit.Sdk;
5+
6+
/// <summary>
7+
/// Custom fact attribute with a default timeout of 20 seconds, allowing tests to be skipped on specific environments.
8+
/// </summary>
9+
/// <seealso cref="Xunit.FactAttribute" />
10+
[AttributeUsage(AttributeTargets.Method)]
11+
[XunitTestCaseDiscoverer("Xunit.Sdk.SkippableFactDiscoverer", "Xunit.SkippableFact")]
12+
internal sealed class IntegrationFactAttribute : FactAttribute
13+
{
14+
private static readonly bool IsOnWsl;
15+
16+
private static readonly bool IsOnCI;
17+
18+
static IntegrationFactAttribute()
19+
{
20+
IsOnWsl = DetectWsl();
21+
IsOnCI = DetectCI();
22+
}
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="IntegrationFactAttribute" /> class.
26+
/// </summary>
27+
public IntegrationFactAttribute()
28+
{
29+
this.Timeout = 20_000;
30+
}
31+
32+
public bool SkipOnWsl { get; set; }
33+
34+
public bool SkipOnCI { get; set; }
35+
36+
/// <summary>
37+
/// Marks the test so that it will not be run, and gets or sets the skip reason
38+
/// </summary>
39+
public override string Skip {
40+
get
41+
{
42+
if (IsOnWsl && this.SkipOnWsl)
43+
{
44+
return "Skipping test on WSL environment.";
45+
}
46+
47+
if (IsOnCI && this.SkipOnCI)
48+
{
49+
return "Skipping test on CI environment.";
50+
}
51+
52+
return base.Skip;
53+
}
54+
set
55+
{
56+
base.Skip = value;
57+
}
58+
}
59+
60+
private static bool DetectWsl()
61+
{
62+
try
63+
{
64+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
65+
{
66+
return false;
67+
}
68+
69+
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_DISTRO_NAME")) ||
70+
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WSL_INTEROP")))
71+
{
72+
return true;
73+
}
74+
75+
return false;
76+
}
77+
catch
78+
{
79+
return false;
80+
}
81+
}
82+
83+
private static bool DetectCI()
84+
{
85+
try
86+
{
87+
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TF_BUILD")) ||
88+
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("GITHUB_ACTIONS")))
89+
{
90+
return true;
91+
}
92+
93+
return false;
94+
}
95+
catch
96+
{
97+
return false;
98+
}
99+
}
100+
}
101+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace ElectronNET.IntegrationTests.Common
2+
{
3+
using ElectronNET.API;
4+
using ElectronNET.API.Entities;
5+
6+
// Base class for integration tests providing shared access to MainWindow and OS platform constants
7+
public abstract class IntegrationTestBase
8+
{
9+
protected IntegrationTestBase(ElectronFixture fixture)
10+
{
11+
Fixture = fixture;
12+
MainWindow = fixture.MainWindow;
13+
}
14+
15+
protected ElectronFixture Fixture { get; }
16+
protected BrowserWindow MainWindow { get; }
17+
18+
// Constants for SupportedOSPlatform attributes
19+
public const string Windows = "Windows";
20+
public const string MacOS = "macOS";
21+
public const string Linux = "Linux";
22+
}
23+
}

src/ElectronNET.IntegrationTests/Common/SkipOnWslFactAttribute.cs

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/ElectronNET.IntegrationTests/ElectronFixture.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ namespace ElectronNET.IntegrationTests
44
using System.Reflection;
55
using ElectronNET.API;
66
using ElectronNET.API.Entities;
7+
using ElectronNET.Common;
78

89
// Shared fixture that starts Electron runtime once
910
[SuppressMessage("ReSharper", "MethodHasAsyncOverload")]
@@ -26,7 +27,7 @@ public async Task InitializeAsync()
2627
await runtimeController.Start();
2728

2829
Console.Error.WriteLine("[ElectronFixture] Waiting for Ready...");
29-
await Task.WhenAny(runtimeController.WaitReadyTask, Task.Delay(TimeSpan.FromSeconds(10)));
30+
await Task.WhenAny(runtimeController.WaitReadyTask, Task.Delay(10.seconds()));
3031

3132
if (!runtimeController.WaitReadyTask.IsCompleted)
3233
{

0 commit comments

Comments
 (0)