Skip to content

Commit d751cbc

Browse files
authored
Fix FrameworkDependentAppLaunch.AppHost_GlobalLocation 'Failed to restore file' CI failure (#116831)
We're hitting an intermittent failure (particularly on Debug) trying to copy over files immediately after the process exits. Since this passes the majority of the time, it does not seem to indicate an actual issue where the files are left in use. - Increase the existing retry we have for doing this restore - Move tests to InstallLocation test class as it fits better there (testing registered and default install locations) - Make InstallLocation test class create one app with test behaviour enabled instead of repeatedly doing it in half of its tests cases. This also removes explicitly running the app with the app directory as the working directory, which doesn't seem interesting for us to keep testing.
1 parent b02b5a0 commit d751cbc

File tree

3 files changed

+82
-107
lines changed

3 files changed

+82
-107
lines changed

src/installer/tests/HostActivation.Tests/FrameworkDependentAppLaunch.cs

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -187,83 +187,17 @@ public void AppHost()
187187
if (Binaries.CetCompat.IsSupported)
188188
Assert.True(Binaries.CetCompat.IsMarkedCompatible(appExe));
189189

190-
// Get the framework location that was built
191-
string builtDotnet = TestContext.BuiltDotNet.BinPath;
192-
193-
// Verify running with the default working directory
194190
Command.Create(appExe)
195191
.CaptureStdErr()
196192
.CaptureStdOut()
197-
.DotNetRoot(builtDotnet, TestContext.BuildArchitecture)
198-
.MultilevelLookup(false)
199-
.Execute()
200-
.Should().Pass()
201-
.And.HaveStdOutContaining("Hello World")
202-
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
203-
204-
205-
// Verify running from within the working directory
206-
Command.Create(appExe)
207-
.WorkingDirectory(sharedTestState.App.Location)
208-
.DotNetRoot(builtDotnet, TestContext.BuildArchitecture)
193+
.DotNetRoot(TestContext.BuiltDotNet.BinPath, TestContext.BuildArchitecture)
209194
.MultilevelLookup(false)
210-
.CaptureStdErr()
211-
.CaptureStdOut()
212195
.Execute()
213196
.Should().Pass()
214197
.And.HaveStdOutContaining("Hello World")
215198
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion);
216199
}
217200

218-
[Theory]
219-
[InlineData(true)]
220-
[InlineData(false)]
221-
public void AppHost_GlobalLocation(bool useRegisteredLocation)
222-
{
223-
string appExe = sharedTestState.App.AppExe;
224-
225-
// Get the framework location that was built
226-
string builtDotnet = TestContext.BuiltDotNet.BinPath;
227-
228-
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(appExe))
229-
{
230-
string architecture = TestContext.BuildArchitecture;
231-
if (useRegisteredLocation)
232-
{
233-
registeredInstallLocationOverride.SetInstallLocation(new (string, string)[] { (architecture, builtDotnet) });
234-
}
235-
236-
// Verify running with the default working directory
237-
Command.Create(appExe)
238-
.CaptureStdErr()
239-
.CaptureStdOut()
240-
.MultilevelLookup(false)
241-
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
242-
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet)
243-
.DotNetRoot(null)
244-
.Execute()
245-
.Should().Pass()
246-
.And.HaveStdOutContaining("Hello World")
247-
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion)
248-
.And.NotHaveStdErr();
249-
250-
// Verify running from within the working directory
251-
Command.Create(appExe)
252-
.CaptureStdErr()
253-
.CaptureStdOut()
254-
.MultilevelLookup(false)
255-
.WorkingDirectory(sharedTestState.App.Location)
256-
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
257-
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, useRegisteredLocation ? null : builtDotnet)
258-
.DotNetRoot(null)
259-
.Execute()
260-
.Should().Pass()
261-
.And.HaveStdOutContaining("Hello World")
262-
.And.HaveStdOutContaining(TestContext.MicrosoftNETCoreAppVersion)
263-
.And.NotHaveStdErr();
264-
}
265-
}
266-
267201
[ConditionalFact(typeof(Binaries.CetCompat), nameof(Binaries.CetCompat.IsSupported))]
268202
public void AppHost_DisableCetCompat()
269203
{

src/installer/tests/HostActivation.Tests/InstallLocation.cs

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void EnvironmentVariable_ArchSpecificDotnetRootIsUsedOverDotnetRoot()
6464
[Fact]
6565
public void EnvironmentVariable_DotNetRootIsUsedOverInstallLocationIfSet()
6666
{
67-
var app = sharedTestState.App.Copy();
67+
var app = sharedTestState.TestBehaviourEnabledApp;
6868
var appExe = app.AppExe;
6969
var arch = TestContext.BuildArchitecture.ToUpper();
7070
var dotnet = TestContext.BuiltDotNet.BinPath;
@@ -87,44 +87,38 @@ public void EnvironmentVariable_DotNetRootIsUsedOverInstallLocationIfSet()
8787
[Fact]
8888
public void EnvironmentVariable_DotnetRootPathDoesNotExist()
8989
{
90-
var app = sharedTestState.App.Copy();
91-
using (TestOnlyProductBehavior.Enable(app.AppExe))
92-
{
93-
Command.Create(app.AppExe)
94-
.EnableTracingAndCaptureOutputs()
95-
.DotNetRoot("non_existent_path")
96-
.MultilevelLookup(false)
97-
.EnvironmentVariable(
98-
Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath,
99-
TestContext.BuiltDotNet.BinPath)
100-
.Execute()
101-
.Should().Pass()
102-
.And.HaveStdErrContaining("Did not find [DOTNET_ROOT] directory [non_existent_path]")
103-
// If DOTNET_ROOT points to a folder that does not exist, we fall back to the global install path.
104-
.And.HaveUsedGlobalInstallLocation(TestContext.BuiltDotNet.BinPath)
105-
.And.HaveStdOutContaining("Hello World");
106-
}
90+
var app = sharedTestState.TestBehaviourEnabledApp;
91+
Command.Create(app.AppExe)
92+
.EnableTracingAndCaptureOutputs()
93+
.DotNetRoot("non_existent_path")
94+
.MultilevelLookup(false)
95+
.EnvironmentVariable(
96+
Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath,
97+
TestContext.BuiltDotNet.BinPath)
98+
.Execute()
99+
.Should().Pass()
100+
.And.HaveStdErrContaining("Did not find [DOTNET_ROOT] directory [non_existent_path]")
101+
// If DOTNET_ROOT points to a folder that does not exist, we fall back to the global install path.
102+
.And.HaveUsedGlobalInstallLocation(TestContext.BuiltDotNet.BinPath)
103+
.And.HaveStdOutContaining("Hello World");
107104
}
108105

109106
[Fact]
110107
public void EnvironmentVariable_DotnetRootPathExistsButHasNoHost()
111108
{
112-
var app = sharedTestState.App.Copy();
113-
using (TestOnlyProductBehavior.Enable(app.AppExe))
114-
{
115-
Command.Create(app.AppExe)
116-
.EnableTracingAndCaptureOutputs()
117-
.DotNetRoot(app.Location)
118-
.MultilevelLookup(false)
119-
.EnvironmentVariable(
120-
Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath,
121-
TestContext.BuiltDotNet.BinPath)
122-
.Execute()
123-
.Should().Fail()
124-
.And.HaveUsedDotNetRootInstallLocation(app.Location, TestContext.BuildRID)
125-
// If DOTNET_ROOT points to a folder that exists we assume that there's a dotnet installation in it
126-
.And.HaveStdErrContaining($"The required library {Binaries.HostFxr.FileName} could not be found.");
127-
}
109+
var app = sharedTestState.TestBehaviourEnabledApp;
110+
Command.Create(app.AppExe)
111+
.EnableTracingAndCaptureOutputs()
112+
.DotNetRoot(app.Location)
113+
.MultilevelLookup(false)
114+
.EnvironmentVariable(
115+
Constants.TestOnlyEnvironmentVariables.GloballyRegisteredPath,
116+
TestContext.BuiltDotNet.BinPath)
117+
.Execute()
118+
.Should().Fail()
119+
.And.HaveUsedDotNetRootInstallLocation(app.Location, TestContext.BuildRID)
120+
// If DOTNET_ROOT points to a folder that exists we assume that there's a dotnet installation in it
121+
.And.HaveStdErrContaining($"The required library {Binaries.HostFxr.FileName} could not be found.");
128122
}
129123

130124
[Fact]
@@ -163,10 +157,49 @@ public void EnvironmentVariable_DotNetInfo_ListEnvironment()
163157
}
164158
}
165159

160+
[Fact]
161+
public void DefaultInstallLocation()
162+
{
163+
TestApp app = sharedTestState.TestBehaviourEnabledApp;
164+
165+
// Ensure no install locations are registered, so the default install location is used
166+
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(app.AppExe))
167+
{
168+
Command.Create(app.AppExe)
169+
.EnableTracingAndCaptureOutputs()
170+
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
171+
.EnvironmentVariable(Constants.TestOnlyEnvironmentVariables.DefaultInstallPath, TestContext.BuiltDotNet.BinPath)
172+
.DotNetRoot(null)
173+
.Execute()
174+
.Should().Pass()
175+
.And.HaveUsedGlobalInstallLocation(TestContext.BuiltDotNet.BinPath);
176+
}
177+
}
178+
179+
[Fact]
180+
public void RegisteredInstallLocation()
181+
{
182+
TestApp app = sharedTestState.TestBehaviourEnabledApp;
183+
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(app.AppExe))
184+
{
185+
registeredInstallLocationOverride.SetInstallLocation(
186+
(TestContext.BuildArchitecture, TestContext.BuiltDotNet.BinPath));
187+
188+
Command.Create(app.AppExe)
189+
.EnableTracingAndCaptureOutputs()
190+
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
191+
.DotNetRoot(null)
192+
.Execute()
193+
.Should().Pass()
194+
.And.HaveUsedRegisteredInstallLocation(TestContext.BuiltDotNet.BinPath)
195+
.And.HaveUsedGlobalInstallLocation(TestContext.BuiltDotNet.BinPath);
196+
}
197+
}
198+
166199
[Fact]
167200
public void RegisteredInstallLocation_ArchSpecificLocationIsPickedFirst()
168201
{
169-
var app = sharedTestState.App.Copy();
202+
var app = sharedTestState.TestBehaviourEnabledApp;
170203
var arch1 = "someArch";
171204
var path1 = "x/y/z";
172205
var arch2 = TestContext.BuildArchitecture;
@@ -202,7 +235,7 @@ public void RegisteredInstallLocation_ArchSpecificLocationIsPickedFirst()
202235
[SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
203236
public void InstallLocationFile_ReallyLongInstallPathIsParsedCorrectly()
204237
{
205-
var app = sharedTestState.App.Copy();
238+
var app = sharedTestState.TestBehaviourEnabledApp;
206239
using (var registeredInstallLocationOverride = new RegisteredInstallLocationOverride(app.AppExe))
207240
{
208241
var reallyLongPath =
@@ -225,9 +258,8 @@ public void InstallLocationFile_ReallyLongInstallPathIsParsedCorrectly()
225258
[SkipOnPlatform(TestPlatforms.Windows, "This test targets the install_location config file which is only used on Linux and macOS.")]
226259
public void InstallLocationFile_MissingFile()
227260
{
228-
var app = sharedTestState.App.Copy();
261+
var app = sharedTestState.TestBehaviourEnabledApp;
229262
using (var testArtifact = TestArtifact.Create("missingInstallLocation"))
230-
using (var testOnlyProductBehavior = TestOnlyProductBehavior.Enable(app.AppExe))
231263
{
232264
string installLocationDirectory = Path.Combine(testArtifact.Location, "installLocationOverride");
233265
Directory.CreateDirectory(installLocationDirectory);
@@ -345,7 +377,7 @@ public void SearchOptions(SearchLocation searchLocation)
345377
}
346378

347379
[Fact]
348-
public void AppHost_AppRelative_MissingPath()
380+
public void SearchOptions_AppRelative_MissingPath()
349381
{
350382
TestApp app = sharedTestState.App.Copy();
351383
app.CreateAppHost(dotNetRootOptions: new HostWriter.DotNetSearchOptions()
@@ -450,16 +482,25 @@ public void SearchOptions_Precedence(SearchLocation expectedResult)
450482
public class SharedTestState : IDisposable
451483
{
452484
public TestApp App { get; }
485+
public TestApp TestBehaviourEnabledApp { get; }
453486

454487
public SharedTestState()
455488
{
456489
App = TestApp.CreateFromBuiltAssets("HelloWorld");
457490
App.CreateAppHost();
491+
492+
TestBehaviourEnabledApp = TestApp.CreateFromBuiltAssets("HelloWorld");
493+
TestBehaviourEnabledApp.CreateAppHost();
494+
495+
// Enable test-only behavior for the app. We don't bother disabling the behaviour later,
496+
// as we just delete the entire app after the tests run.
497+
_ = TestOnlyProductBehavior.Enable(TestBehaviourEnabledApp.AppExe);
458498
}
459499

460500
public void Dispose()
461501
{
462502
App?.Dispose();
503+
TestBehaviourEnabledApp?.Dispose();
463504
}
464505
}
465506
}

src/installer/tests/TestUtils/TestFileBackup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private static void CopyOverDirectory(string source, string destination)
116116
}
117117
}
118118

119-
private static void RetryOnIOError(Func<bool> action, string errorMessage, int maxRetries = 5)
119+
private static void RetryOnIOError(Func<bool> action, string errorMessage, int maxRetries = 25)
120120
{
121121
IOException exception = null;
122122
for (int i = 0; i < maxRetries; i++)

0 commit comments

Comments
 (0)