Skip to content

Commit eb820dd

Browse files
authored
Enhance test isolation by adding copy constructors and ensuring fresh instances of BrowserVersion across tests (#410)
1 parent 8a49fec commit eb820dd

File tree

3 files changed

+46
-6
lines changed

3 files changed

+46
-6
lines changed

Saucery.Core/DataSources/SauceryTestData.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,31 @@ private static void ExpandAndFilter(List<SaucePlatform> platforms, PlatformConfi
2424
BrowserVersions = platformConfigurator.FilterAll(expandedPlatforms);
2525
}
2626

27-
public static IEnumerable<BrowserVersion> Items =>
27+
/// <summary>
28+
/// Returns fresh instances to ensure test isolation.
29+
/// Each access creates new BrowserVersion instances to prevent state leakage across tests.
30+
/// </summary>
31+
public static IEnumerable<BrowserVersion> Items =>
2832
BrowserVersions!
29-
.Select(x => x)
33+
.Select(x => new BrowserVersion(x))
3034
.AsEnumerable();
3135

36+
/// <summary>
37+
/// Returns platforms wrapped in object arrays, with fresh instances for test isolation.
38+
/// </summary>
3239
protected static IEnumerable<object[]> GetAllPlatforms() {
3340
List<object[]> allPlatforms = [];
3441
allPlatforms.AddRange(Items.Select(platform => (object[])[platform]));
3542

3643
return allPlatforms.AsEnumerable();
3744
}
3845

46+
/// <summary>
47+
/// Returns factory functions that create fresh BrowserVersion instances when invoked.
48+
/// This ensures each test gets its own independent instance, preventing state leakage.
49+
/// </summary>
3950
protected static List<Func<BrowserVersion>> GetAllPlatformsAsFunc() =>
40-
[.. Items.Select(platform => (Func<BrowserVersion>)(() => platform))];
51+
[.. BrowserVersions!.Select(template => (Func<BrowserVersion>)(() => new BrowserVersion(template)))];
4152
}
4253
/*
4354
* Copyright Andrew Gray, SauceForge

Saucery.Core/Dojo/BrowserVersion.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,26 @@ public BrowserVersion(SaucePlatform platform) {
8484
ScreenResolutions = [];
8585
}
8686

87+
/// <summary>
88+
/// Copy constructor - creates a new independent instance with the same values.
89+
/// This ensures test isolation by preventing shared mutable state.
90+
/// </summary>
91+
public BrowserVersion(BrowserVersion other) {
92+
Os = other.Os;
93+
PlatformNameForOption = other.PlatformNameForOption;
94+
BrowserName = other.BrowserName;
95+
Name = other.Name;
96+
AutomationBackend = other.AutomationBackend;
97+
DeviceName = other.DeviceName;
98+
RecommendedAppiumVersion = other.RecommendedAppiumVersion;
99+
SupportedBackendVersions = [..other.SupportedBackendVersions];
100+
DeprecatedBackendVersions = [..other.DeprecatedBackendVersions];
101+
DeviceOrientation = other.DeviceOrientation;
102+
ScreenResolution = other.ScreenResolution;
103+
PlatformType = other.PlatformType;
104+
ScreenResolutions = [..other.ScreenResolutions];
105+
}
106+
87107
/// <summary>
88108
/// Generates a unique test name from this configuration and the test context.
89109
/// This does not mutate the BrowserVersion instance.

Saucery.Core/Dojo/DojoExtensions.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static IList<T> GetPlatform<T>(this List<PlatformBase> availablePlatforms
122122
b.Os.Equals(sp.Os, StringComparison.Ordinal) &&
123123
b.Name.Equals(sp.Browser, StringComparison.OrdinalIgnoreCase));
124124

125-
return sp.ScreenResolution == string.Empty
125+
var template = sp.ScreenResolution == string.Empty
126126
? browser?.BrowserVersions.FirstOrDefault(v =>
127127
v.Os.Equals(sp.Os, StringComparison.Ordinal) &&
128128
v.BrowserName.Equals(sp.Browser, StringComparison.OrdinalIgnoreCase) &&
@@ -132,6 +132,9 @@ public static IList<T> GetPlatform<T>(this List<PlatformBase> availablePlatforms
132132
v.BrowserName.Equals(sp.Browser, StringComparison.OrdinalIgnoreCase) &&
133133
v.Name!.Equals(sp.BrowserVersion, StringComparison.OrdinalIgnoreCase) &&
134134
v.ScreenResolutions.Contains(sp.ScreenResolution));
135+
136+
// Return a new instance instead of a reference to prevent state leakage across tests
137+
return template != null ? new BrowserVersion(template) : null;
135138
}
136139

137140
public static BrowserVersion? FindAndroidBrowser(this List<PlatformBase> platforms, SaucePlatform sp)
@@ -160,12 +163,15 @@ public static IList<T> GetPlatform<T>(this List<PlatformBase> availablePlatforms
160163
b.Os.Equals(sp.Os, StringComparison.Ordinal) &&
161164
b.DeviceName.Equals(sp.LongName, StringComparison.Ordinal));
162165

163-
return browser?.BrowserVersions.Count == 1
166+
var template = browser?.BrowserVersions.Count == 1
164167
? browser.BrowserVersions[0]
165168
: browser?.BrowserVersions.FirstOrDefault(v =>
166169
v.Os.Equals(sp.Os, StringComparison.Ordinal) &&
167170
v.DeviceName.Equals(sp.LongName, StringComparison.Ordinal) &&
168171
v.Name!.Equals(sp.BrowserVersion, StringComparison.Ordinal));
172+
173+
// Return a new instance instead of a reference to prevent state leakage across tests
174+
return template != null ? new BrowserVersion(template) : null;
169175
}
170176

171177
public static PlatformBase? FindAndroidPlatform(this List<PlatformBase> platforms, SaucePlatform sp)
@@ -212,12 +218,15 @@ public static IList<T> GetPlatform<T>(this List<PlatformBase> availablePlatforms
212218
b.PlatformNameForOption.Equals(sp.Os, StringComparison.Ordinal) &&
213219
b.DeviceName.Equals(sp.LongName, StringComparison.Ordinal));
214220

215-
return browser?.BrowserVersions.Count == 1
221+
var template = browser?.BrowserVersions.Count == 1
216222
? browser.BrowserVersions[0]
217223
: browser?.BrowserVersions.FirstOrDefault(v =>
218224
v.PlatformNameForOption.Equals(sp.Os, StringComparison.Ordinal) &&
219225
v.DeviceName.Equals(sp.LongName, StringComparison.Ordinal) &&
220226
v.Name!.Equals(sp.BrowserVersion, StringComparison.Ordinal));
227+
228+
// Return a new instance instead of a reference to prevent state leakage across tests
229+
return template != null ? new BrowserVersion(template) : null;
221230
}
222231

223232
public static PlatformBase? FindIOSPlatform(this List<PlatformBase> platforms, SaucePlatform sp)

0 commit comments

Comments
 (0)