Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions AndroidSdk.Tests/SdkTool_PreserveHome_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#nullable enable
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace AndroidSdk.Tests;

/// <summary>
/// Tests that SdkTool and AndroidSdkManager preserve explicitly specified
/// home directories even when the directory does not yet exist on disk.
/// This is critical for the Acquire/DownloadSdk workflow where the SDK
/// must be downloaded to a new directory.
/// </summary>
public class SdkTool_PreserveHome_Tests : TestsBase
{
public SdkTool_PreserveHome_Tests(ITestOutputHelper outputHelper)
: base(outputHelper)
{
}

[Fact]
public void SdkManager_PreservesHome_WhenDirectoryDoesNotExist()
{
var nonExistentPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", "NonExistent", Guid.NewGuid().ToString("N"));
Assert.False(Directory.Exists(nonExistentPath));

var mgr = new SdkManager(new SdkManagerToolOptions
{
AndroidSdkHome = new DirectoryInfo(nonExistentPath),
SkipVersionCheck = true
});

Assert.NotNull(mgr.AndroidSdkHome);
Assert.Equal(nonExistentPath, mgr.AndroidSdkHome!.FullName);
}

[Fact]
public void AndroidSdkManager_PreservesHome_WhenDirectoryDoesNotExist()
{
var nonExistentPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", "NonExistent", Guid.NewGuid().ToString("N"));
Assert.False(Directory.Exists(nonExistentPath));

var mgr = new AndroidSdkManager(new DirectoryInfo(nonExistentPath));

Assert.NotNull(mgr.Home);
Assert.Equal(nonExistentPath, mgr.Home!.FullName);
}

[Fact]
public async Task SdkManager_DownloadSdk_DoesNotThrow_WhenDirectoryDoesNotExist()
{
var nonExistentPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", "NonExistent", Guid.NewGuid().ToString("N"));
Assert.False(Directory.Exists(nonExistentPath));

var mgr = new SdkManager(new SdkManagerToolOptions
{
AndroidSdkHome = new DirectoryInfo(nonExistentPath),
SkipVersionCheck = true
});

// DownloadSdk should not throw DirectoryNotFoundException
// because AndroidSdkHome should be preserved
var ex = await Record.ExceptionAsync(() => mgr.DownloadSdk());

// If it throws, it should NOT be the "Directory was not specified" error
if (ex != null)
{
Assert.DoesNotContain("was not specified", ex.Message);
}

// Clean up if anything was created
try { if (Directory.Exists(nonExistentPath)) Directory.Delete(nonExistentPath, true); } catch { }
}

[Fact]
public void SdkManager_UsesLocatedPath_WhenDirectoryExists()
{
// When the directory exists and contains an SDK, Locate should find it
var existingPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", "ExistingDir", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(existingPath);

try
{
var mgr = new SdkManager(new SdkManagerToolOptions
{
AndroidSdkHome = new DirectoryInfo(existingPath),
SkipVersionCheck = true
});

// Should still resolve to the existing path
Assert.NotNull(mgr.AndroidSdkHome);
Assert.Equal(existingPath, mgr.AndroidSdkHome!.FullName);
}
finally
{
try { Directory.Delete(existingPath, true); } catch { }
}
}

[Fact]
public void AndroidSdkManager_UsesLocatedPath_WhenDirectoryExists()
{
var existingPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", "ExistingDir", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(existingPath);

try
{
var mgr = new AndroidSdkManager(new DirectoryInfo(existingPath));

Assert.NotNull(mgr.Home);
Assert.Equal(existingPath, mgr.Home!.FullName);
}
finally
{
try { Directory.Delete(existingPath, true); } catch { }
}
}

[Fact]
public void SdkManager_HomeIsNull_WhenNoPathSpecified()
{
// When no path is given and no SDK is found via env vars,
// Home might be null or point to a discovered SDK — either is valid.
// The key invariant: if Locate() returns something, use it.
var mgr = new SdkManager(new SdkManagerToolOptions
{
AndroidSdkHome = null,
SkipVersionCheck = true
});

// AndroidSdkHome should be whatever Locate() found (possibly null)
var located = new SdkLocator().Locate()?.FirstOrDefault();
if (located != null)
Assert.Equal(located.FullName, mgr.AndroidSdkHome?.FullName);
else
Assert.Null(mgr.AndroidSdkHome);
}
}
3 changes: 2 additions & 1 deletion AndroidSdk/AndroidSdkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public static IEnumerable<DirectoryInfo> FindHome(string specificHome = null, pa

public AndroidSdkManager(DirectoryInfo home = null)
{
Home = new SdkLocator().Locate(home?.FullName)?.FirstOrDefault();
Home = home
?? new SdkLocator().Locate()?.FirstOrDefault();

SdkManager = new SdkManager(new SdkManagerToolOptions { AndroidSdkHome = Home });
AvdManager = new AvdManager(Home);
Expand Down
8 changes: 5 additions & 3 deletions AndroidSdk/SdkTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ public SdkTool(string? androidSdkHome)
public SdkTool(DirectoryInfo? androidSdkHome)
: this(new SdkToolOptions { AndroidSdkHome = androidSdkHome })
{
AndroidSdkHome = new SdkLocator().Locate(androidSdkHome?.FullName)?.FirstOrDefault();
Jdks = new JdkLocator().LocateJdk()?.ToArray() ?? new JdkInfo[0];
}

public SdkTool(SdkToolOptions? options)
{
options ??= new();
AndroidSdkHome = new SdkLocator().Locate(options.AndroidSdkHome?.FullName)?.FirstOrDefault();
// When a path is explicitly provided, always honor it — the caller
// may intend to download/create the SDK there. Only auto-discover
// when no path is specified.
AndroidSdkHome = options.AndroidSdkHome
?? new SdkLocator().Locate()?.FirstOrDefault();
Jdks = new JdkLocator().LocateJdk()?.ToArray() ?? new JdkInfo[0];
}

Expand Down
Loading