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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ permissions:
jobs:
test:
name: Test
runs-on: macos-13
runs-on: macos-15-intel
steps:
- name: Checkout
uses: actions/checkout@v2
Expand Down
24 changes: 8 additions & 16 deletions AndroidSdk.Tests/ApkReader_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,23 @@

namespace AndroidSdk.Tests;

public class ApkReader_Tests : TestsBase
public class ApkReader_Tests(ITestOutputHelper outputHelper) : TestsBase(outputHelper)
{
public ApkReader_Tests(ITestOutputHelper outputHelper) : base(outputHelper)
{
ApkFile = Path.GetFullPath(Path.Combine(TestDataDirectory, "com.companyname.mauiapp12345-Signed.apk"));
}

public readonly string ApkFile;

[Fact]
public void ReadPackageId()
{
var reader = new ApkReader(ApkFile);
var reader = new ApkReader(StaticAppApkPath);

var packageId = reader.ReadManifest().Manifest.PackageId;

Assert.NotNull(packageId);
Assert.Equal("com.companyname.mauiapp12345", packageId);
Assert.Equal(StaticAppPackageName, packageId);
}

[Fact]
public void ReadVersionName()
{
var reader = new ApkReader(ApkFile);
var reader = new ApkReader(StaticAppApkPath);

var versionName = reader.ReadManifest().Manifest.VersionName;

Expand All @@ -45,7 +38,7 @@ public void ReadVersionName()
[Fact]
public void ReadVersionCode()
{
var reader = new ApkReader(ApkFile);
var reader = new ApkReader(StaticAppApkPath);

var versionCode = reader.ReadManifest().Manifest.VersionCode;

Expand All @@ -56,7 +49,7 @@ public void ReadVersionCode()
[Fact]
public void ReadMinSdkVersion()
{
var reader = new ApkReader(ApkFile);
var reader = new ApkReader(StaticAppApkPath);

var minSdkVersion = reader.ReadManifest().Manifest.UsesSdk.MinSdkVersion;

Expand All @@ -66,7 +59,7 @@ public void ReadMinSdkVersion()
[Fact]
public void ReadTargetSdkVersion()
{
var reader = new ApkReader(ApkFile);
var reader = new ApkReader(StaticAppApkPath);

var targetSdkVersion = reader.ReadManifest().Manifest.UsesSdk.TargetSdkVersion;

Expand All @@ -76,11 +69,10 @@ public void ReadTargetSdkVersion()
[Fact]
public void ReadMaxSdkVersion()
{
var reader = new ApkReader(ApkFile);
var reader = new ApkReader(StaticAppApkPath);

var maxSdkVersion = reader.ReadManifest().Manifest.UsesSdk.MaxSdkVersion;

Assert.Equal(0, maxSdkVersion);
}

}
76 changes: 45 additions & 31 deletions AndroidSdk.Tests/AvdLocator_Tests.cs
Original file line number Diff line number Diff line change
@@ -1,93 +1,94 @@
using System;
#nullable enable
using System;
using System.IO;
using Xunit;

namespace AndroidSdk.Tests;

/// <summary>
/// Tests AVD location precedence and AVD file discovery behavior.
/// </summary>
public class AvdLocator_Tests
{
[Fact]
public void LocatedPathForAndroidSdkHomeIncludesDotAndroid()
{
var oldVal = Environment.GetEnvironmentVariable("ANDROID_SDK_HOME");

var tempRoot = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AvdLocator_Tests), nameof(LocatedPathForAndroidSdkHomeIncludesDotAndroid));
var tempRoot = CreateTempRoot(nameof(LocatedPathForAndroidSdkHomeIncludesDotAndroid));
var expectedPath = Path.Combine(tempRoot, ".android", "avd");
Directory.CreateDirectory(expectedPath);
using var envVars = new EnvironmentVariablesScope(
("ANDROID_AVD_ROOT", null),
("ANDROID_AVD_HOME", null),
("ANDROID_USER_HOME", null),
("ANDROID_PREFS_ROOT", null),
("ANDROID_SDK_HOME", tempRoot));

try
{
Environment.SetEnvironmentVariable("ANDROID_SDK_HOME", tempRoot);

var l = new AvdLocator();
var paths = l.PreferredPaths();

Assert.Contains(expectedPath, paths);
Assert.Equal(expectedPath, paths[0]);
}
finally
{
Environment.SetEnvironmentVariable("ANDROID_SDK_HOME", oldVal);
if (Directory.Exists(tempRoot))
Directory.Delete(tempRoot, true);
CleanupDirectory(tempRoot);
}
}

[Fact]
public void LocatedPathForAndroidPrefsRootIncludesDotAndroid()
{
var oldVal = Environment.GetEnvironmentVariable("ANDROID_PREFS_ROOT");

var tempRoot = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AvdLocator_Tests), nameof(LocatedPathForAndroidPrefsRootIncludesDotAndroid));
var tempRoot = CreateTempRoot(nameof(LocatedPathForAndroidPrefsRootIncludesDotAndroid));
var expectedPath = Path.Combine(tempRoot, ".android", "avd");
Directory.CreateDirectory(expectedPath);
using var envVars = new EnvironmentVariablesScope(
("ANDROID_AVD_ROOT", null),
("ANDROID_AVD_HOME", null),
("ANDROID_USER_HOME", null),
("ANDROID_PREFS_ROOT", tempRoot));

try
{
Environment.SetEnvironmentVariable("ANDROID_PREFS_ROOT", tempRoot);

var l = new AvdLocator();
var paths = l.PreferredPaths();

Assert.Contains(expectedPath, paths);
Assert.Equal(expectedPath, paths[0]);
}
finally
{
Environment.SetEnvironmentVariable("ANDROID_PREFS_ROOT", oldVal);
if (Directory.Exists(tempRoot))
Directory.Delete(tempRoot, true);
CleanupDirectory(tempRoot);
}
}

[Fact]
public void LocatedPathForAndroidUserHomeUsesAvdSuffix()
{
var oldVal = Environment.GetEnvironmentVariable("ANDROID_USER_HOME");

var tempRoot = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AvdLocator_Tests), nameof(LocatedPathForAndroidUserHomeUsesAvdSuffix));
var tempRoot = CreateTempRoot(nameof(LocatedPathForAndroidUserHomeUsesAvdSuffix));
var expectedPath = Path.Combine(tempRoot, "avd");
Directory.CreateDirectory(expectedPath);
using var envVars = new EnvironmentVariablesScope(
("ANDROID_AVD_ROOT", null),
("ANDROID_AVD_HOME", null),
("ANDROID_USER_HOME", tempRoot));

try
{
Environment.SetEnvironmentVariable("ANDROID_USER_HOME", tempRoot);

var l = new AvdLocator();
var paths = l.PreferredPaths();

Assert.Contains(expectedPath, paths);
Assert.Equal(expectedPath, paths[0]);
}
finally
{
Environment.SetEnvironmentVariable("ANDROID_USER_HOME", oldVal);
if (Directory.Exists(tempRoot))
Directory.Delete(tempRoot, true);
CleanupDirectory(tempRoot);
}
}

[Fact]
public void ListAvdsFindsAvdsFromFileSystem()
{
var tempRoot = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AvdLocator_Tests), nameof(ListAvdsFindsAvdsFromFileSystem));
var tempRoot = CreateTempRoot(nameof(ListAvdsFindsAvdsFromFileSystem));
var avdHome = Path.Combine(tempRoot, "avd-home");

// Create a fake AVD structure
Expand All @@ -108,8 +109,21 @@ public void ListAvdsFindsAvdsFromFileSystem()
}
finally
{
if (Directory.Exists(tempRoot))
Directory.Delete(tempRoot, true);
CleanupDirectory(tempRoot);
}
}

static string CreateTempRoot(string testName)
=> Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AvdLocator_Tests), testName);

static void CleanupDirectory(string path)
{
try
{
if (Directory.Exists(path))
Directory.Delete(path, true);
}
catch (IOException) { }
catch (UnauthorizedAccessException) { }
}
}
8 changes: 5 additions & 3 deletions AndroidSdk.Tests/Helpers/AndroidSdkManagerCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

namespace AndroidSdk.Tests;

[CollectionDefinition(Name)]
/// <summary>
/// Collection fixture for Android SDK manager tests.
/// It provides a shared AndroidSdkManagerFixture instance for all tests in the collection and ensures proper cleanup of any temporary SDKs created during testing.
/// </summary>
[CollectionDefinition(nameof(AndroidSdkManagerCollection))]
public class AndroidSdkManagerCollection : ICollectionFixture<AndroidSdkManagerFixture>
{
public const string Name = "Android SDK Manager";

// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
Expand Down
36 changes: 25 additions & 11 deletions AndroidSdk.Tests/Helpers/AndroidSdkManagerFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -8,18 +9,18 @@

namespace AndroidSdk.Tests;

public class AndroidSdkManagerFixture : IAsyncLifetime
/// <summary>
/// Shared SDK fixture that can use a global SDK when enabled and otherwise
/// provisions an isolated temp SDK once per collection and
/// only cleans up SDK directories that it created and owns.
/// </summary>
public class AndroidSdkManagerFixture(IMessageSink messageSink) : IAsyncLifetime
{
private string? tempSdkPath;

public const bool TryUsingGlobalSdk = true;
private const bool TryUsingGlobalSdk = true;

public AndroidSdkManagerFixture(IMessageSink messageSink)
{
MessageSink = messageSink;
}
private string? tempSdkPath;

public IMessageSink MessageSink { get; }
public IMessageSink MessageSink { get; } = messageSink;

public DirectoryInfo AndroidSdkHome { get; private set; } = null!;

Expand All @@ -32,8 +33,21 @@ public async Task InitializeAsync()

public Task DisposeAsync()
{
if (!string.IsNullOrEmpty(tempSdkPath) && Directory.Exists(tempSdkPath))
if (string.IsNullOrEmpty(tempSdkPath) || !Directory.Exists(tempSdkPath))
return Task.CompletedTask;

try
{
Directory.Delete(tempSdkPath, true);
}
catch (IOException ex)
{
MessageSink.OnMessage(new DiagnosticMessage("Failed to delete TEMP android sdk at {0}: {1}", tempSdkPath, ex.Message));
}
catch (UnauthorizedAccessException ex)
{
MessageSink.OnMessage(new DiagnosticMessage("Failed to delete TEMP android sdk at {0}: {1}", tempSdkPath, ex.Message));
}

return Task.CompletedTask;
}
Expand All @@ -56,7 +70,7 @@ async Task<AndroidSdkManager> GetAndroidSdk()

if (AndroidSdkHome == null || !AndroidSdkHome.Exists)
{
tempSdkPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AndroidSdkManagerFixture), "android-sdk");
tempSdkPath = Path.Combine(Path.GetTempPath(), "AndroidSdk.Tests", nameof(AndroidSdkManagerFixture), Guid.NewGuid().ToString("N"), "android-sdk");

Directory.CreateDirectory(tempSdkPath);

Expand Down
20 changes: 7 additions & 13 deletions AndroidSdk.Tests/Helpers/AndroidSdkManagerTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,14 @@
namespace AndroidSdk.Tests;

/// <summary>
/// A base class for all tests that will require the Android SDK to be installed
/// and ready to use without having to first download and update it.
/// Base class for tests that require a ready Android SDK fixture.
/// It only injects SDK dependencies and does not own SDK lifecycle/setup state.
/// </summary>
[Collection(AndroidSdkManagerCollection.Name)]
public abstract class AndroidSdkManagerTestsBase : TestsBase
[Collection(nameof(AndroidSdkManagerCollection))]
public abstract class AndroidSdkManagerTestsBase(ITestOutputHelper outputHelper, AndroidSdkManagerFixture fixture)
: TestsBase(outputHelper)
{
public AndroidSdkManagerTestsBase(ITestOutputHelper outputHelper, AndroidSdkManagerFixture fixture)
: base(outputHelper)
{
AndroidSdkHome = fixture.AndroidSdkHome;
Sdk = fixture.Sdk;
}
public DirectoryInfo AndroidSdkHome { get; } = fixture.AndroidSdkHome;

public DirectoryInfo AndroidSdkHome { get; }

public AndroidSdkManager Sdk { get; }
public AndroidSdkManager Sdk { get; } = fixture.Sdk;
}
8 changes: 5 additions & 3 deletions AndroidSdk.Tests/Helpers/AvdManagerTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace AndroidSdk.Tests;
/// A base class for all tests that will be working with AVDs and want to ensure the
/// AVD environemnt is clean and free of any previous AVDs.
/// </summary>
public abstract class AvdManagerTestsBase : AndroidSdkManagerTestsBase, IDisposable
public abstract class AvdManagerTestsBase : AndroidSdkManagerTestsBase
{
readonly string oldAndroidAvdHome;
readonly string tempAndroidAvdHome;
Expand All @@ -23,16 +23,18 @@ public AvdManagerTestsBase(ITestOutputHelper outputHelper, AndroidSdkManagerFixt
if (!string.IsNullOrEmpty(oldAndroidAvdHome) && oldAndroidAvdHome.StartsWith(tempRoot))
throw new InvalidOperationException("ANDROID_AVD_HOME was not un set from a previous test run.");

tempAndroidAvdHome = Path.Combine(tempRoot, "AndroidSdk.Tests", GetType().Name, "android-avd-home");
tempAndroidAvdHome = Path.Combine(tempRoot, "AndroidSdk.Tests", GetType().Name, Guid.NewGuid().ToString("N"), "android-avd-home");
RecreateDir(tempAndroidAvdHome);

Environment.SetEnvironmentVariable("ANDROID_AVD_HOME", tempAndroidAvdHome);
}

public virtual void Dispose()
public override void Dispose()
{
Environment.SetEnvironmentVariable("ANDROID_AVD_HOME", oldAndroidAvdHome);

DeleteDir(tempAndroidAvdHome);

base.Dispose();
}
}
Loading
Loading