diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs index 807e50a8568..98a918abf73 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs @@ -765,12 +765,15 @@ static void AssertApplicationConfigIsIdentical (ApplicationConfig_MonoVM firstAp Assert.AreEqual (firstAppConfig.android_package_name, secondAppConfig.android_package_name, $"Field 'android_package_name' has different value in environment file '{secondEnvFile}' than in environment file '{firstEnvFile}'"); } - public static List GatherEnvironmentFiles (string outputDirectoryRoot, string supportedAbis, bool required) + // TODO: remove the default from the `runtime` parameter once all tests are updated + public static List GatherEnvironmentFiles (string outputDirectoryRoot, string supportedAbis, bool required, AndroidRuntime runtime = AndroidRuntime.CoreCLR) { var environmentFiles = new List (); + bool isNativeAOT = runtime == AndroidRuntime.NativeAOT; foreach (string abi in supportedAbis.Split (';')) { - string envFilePath = Path.Combine (outputDirectoryRoot, "android", $"environment.{abi}.ll"); + string prefixDir = isNativeAOT ? MonoAndroidHelper.AbiToRid (abi) : String.Empty; + string envFilePath = Path.Combine (outputDirectoryRoot, prefixDir, "android", $"environment.{abi}.ll"); Assert.IsTrue (File.Exists (envFilePath), $"Environment file {envFilePath} does not exist"); environmentFiles.Add (new EnvironmentFile (envFilePath, abi)); diff --git a/tests/MSBuildDeviceIntegration/Tests/BundleToolNoAbiSplitTests.cs b/tests/MSBuildDeviceIntegration/Tests/BundleToolNoAbiSplitTests.cs index 95f172e8e6f..eca4ab420d5 100644 --- a/tests/MSBuildDeviceIntegration/Tests/BundleToolNoAbiSplitTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/BundleToolNoAbiSplitTests.cs @@ -9,8 +9,7 @@ namespace Xamarin.Android.Build.Tests { - [TestFixture (AndroidRuntime.MonoVM)] - [TestFixture (AndroidRuntime.CoreCLR)] + [TestFixtureSource (nameof (Get_ConstructorParameters))] [Category ("UsesDevice")] public class BundleToolNoAbiSplitTests : DeviceTest { @@ -37,6 +36,11 @@ public class BundleToolNoAbiSplitTests : DeviceTest }"; readonly AndroidRuntime runtime; + static Array Get_ConstructorParameters () + { + return Enum.GetValues (typeof (AndroidRuntime)); + } + public BundleToolNoAbiSplitTests (AndroidRuntime runtime) { this.runtime = runtime; @@ -127,19 +131,22 @@ public void OneTimeSetUp () List envFiles = EnvironmentHelper.GatherEnvironmentFiles ( objPath, String.Join (";", Abis), - true + true, + runtime ); - EnvironmentHelper.IApplicationConfig app_config = EnvironmentHelper.ReadApplicationConfig (envFiles, runtime); + if (runtime != AndroidRuntime.NativeAOT) { // NAOT doesn't have ApplicationConfig + EnvironmentHelper.IApplicationConfig app_config = EnvironmentHelper.ReadApplicationConfig (envFiles, runtime); - Assert.That (app_config, Is.Not.Null, "application_config must be present in the environment files"); + Assert.That (app_config, Is.Not.Null, "application_config must be present in the environment files"); - bool ignoreSplitConfigs = runtime switch { - AndroidRuntime.MonoVM => ((EnvironmentHelper.ApplicationConfig_MonoVM)app_config).ignore_split_configs, - AndroidRuntime.CoreCLR => ((EnvironmentHelper.ApplicationConfig_CoreCLR)app_config).ignore_split_configs, - _ => throw new NotSupportedException ($"Unsupported runtime '{runtime}'") - }; - Assert.AreEqual (ignoreSplitConfigs, true, $"App config should indicate that split configs must be ignored"); + bool ignoreSplitConfigs = runtime switch { + AndroidRuntime.MonoVM => ((EnvironmentHelper.ApplicationConfig_MonoVM)app_config).ignore_split_configs, + AndroidRuntime.CoreCLR => ((EnvironmentHelper.ApplicationConfig_CoreCLR)app_config).ignore_split_configs, + _ => throw new NotSupportedException ($"Unsupported runtime '{runtime}'") + }; + Assert.AreEqual (ignoreSplitConfigs, true, $"App config should indicate that split configs must be ignored"); + } } [TearDown] @@ -161,6 +168,18 @@ public void OneTimeTearDown () [Test] public void InstallAndRun () { + // TODO: fix under NativeAOT. Currently fails with an exception at run time: + // + // FATAL UNHANDLED EXCEPTION: System.InvalidCastException: Unable to convert instance of type 'AndroidX.AppCompat.Widget.AppCompatImageButton' to type 'AndroidX.AppCompat.Widget.Toolbar'. + // at Java.Interop.JavaObjectExtensions._JavaCast[TResult](IJavaObject) + 0x165 + // at Android.Runtime.Extensions.JavaCast[TResult](IJavaObject) + 0x10 + // at Xamarin.Forms.Platform.Android.FormsAppCompatActivity.OnCreate(Bundle, ActivationFlags) + 0x601 + // at UnnamedProject.MainActivity.OnCreate(Bundle savedInstanceState) + 0x3c + // at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_(IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState) + 0x72 + // + if (runtime == AndroidRuntime.NativeAOT) { + Assert.Ignore ("NativeAOT crashes with an InvalidCastException exception"); + } Assert.IsTrue (appBuilder.Install (app), "Install should have succeeded."); RunProjectAndAssert (app, appBuilder); Assert.True ( diff --git a/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs b/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs index f41326dfabe..c970517884d 100644 --- a/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/BundleToolTests.cs @@ -48,11 +48,10 @@ static IEnumerable Get_FixtureArgs () new object[] { false }, new object[] { true }, }; - var runtimes = new [] { AndroidRuntime.MonoVM, AndroidRuntime.CoreCLR }; var ret = new List (); foreach (object[] args in fixtureArgs) { - foreach (AndroidRuntime runtime in runtimes) { + foreach (AndroidRuntime runtime in Enum.GetValues (typeof (AndroidRuntime))) { ret.Add (new object[] { args[0], runtime, @@ -72,10 +71,15 @@ public BundleToolTests (bool usesAssemblyBlobs, AndroidRuntime runtime) [OneTimeSetUp] public void OneTimeSetUp () { + const bool isRelease = true; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + var path = Path.Combine ("temp", TestName); lib = new XamarinAndroidLibraryProject { ProjectName = "Localization", - IsRelease = true, + IsRelease = isRelease, OtherBuildItems = { new BuildItem ("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents ("Cancel") @@ -91,7 +95,7 @@ public void OneTimeSetUp () var bytes = new byte [1024]; app = new XamarinFormsMapsApplicationProject { - IsRelease = true, + IsRelease = isRelease, AotAssemblies = false, // Release defaults to Profiled AOT for .NET 6 PackageName = "com.xamarin.bundletooltests", }; @@ -186,6 +190,11 @@ public void BaseZip () expectedFiles.Add ("root/play-services-tasks.properties"); foreach (var abi in Abis) { + if (runtime == AndroidRuntime.NativeAOT) { + expectedFiles.Add ($"lib/{abi}/libUnnamedProject.so"); + continue; + } + // All assemblies are in per-abi directories now if (usesAssemblyBlobs) { expectedFiles.Add ($"{blobEntryPrefix}{abi}/lib_Java.Interop.dll.so"); @@ -250,6 +259,11 @@ public void AppBundle () expectedFiles.Add ("base/root/play-services-tasks.properties"); foreach (var abi in Abis) { + if (runtime == AndroidRuntime.NativeAOT) { + expectedFiles.Add ($"base/lib/{abi}/libUnnamedProject.so"); + continue; + } + // All assemblies are in per-abi directories now if (usesAssemblyBlobs) { expectedFiles.Add ($"{blobEntryPrefix}{abi}/lib_Java.Interop.dll.so"); diff --git a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs index 0937b4d60cf..21355ab78eb 100755 --- a/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs +++ b/tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs @@ -10,6 +10,7 @@ using Xamarin.ProjectTools; using System.Collections.Generic; using Microsoft.Build.Framework; +using Xamarin.Android.Tasks; namespace Xamarin.Android.Build.Tests { @@ -52,13 +53,30 @@ int FindTextInFile (string file, string text) } [Test] - public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isRelease, [Values (false, true)] bool extractNativeLibs, [Values (false, true)] bool useEmbeddedDex) + public void ApplicationRunsWithoutDebugger ([Values] bool isRelease, [Values] bool extractNativeLibs, [Values] bool useEmbeddedDex, [Values] AndroidRuntime runtime) { + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + + // TODO: NativeAOT fails with the following exception: + // + // FATAL UNHANDLED EXCEPTION: System.InvalidCastException: Unable to convert instance of type 'AndroidX.AppCompat.Widget.AppCompatImageButton' to type 'AndroidX.AppCompat.Widget.Toolbar'. + // at Java.Interop.JavaObjectExtensions._JavaCast[TResult](IJavaObject) + 0x190 + // at Android.Runtime.Extensions.JavaCast[TResult](IJavaObject) + 0x18 + // at Xamarin.Forms.Platform.Android.FormsAppCompatActivity.OnCreate(Bundle, ActivationFlags) + 0x5bc + // at UnnamedProject.MainActivity.OnCreate(Bundle savedInstanceState) + 0x4c + // at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_(IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState) + 0x7c + if (runtime == AndroidRuntime.NativeAOT) { + Assert.Ignore ("NativeAOT currently crashes with an exception."); + } + SwitchUser (); var proj = new XamarinFormsAndroidApplicationProject () { IsRelease = isRelease, }; + proj.SetRuntime (runtime); if (isRelease || !TestEnvironment.CommercialBuildAvailable) { proj.SetAndroidSupportedAbis (DeviceAbi); } @@ -82,15 +100,44 @@ public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isReleas } [Test] - public void ClassLibraryMainLauncherRuns ([Values (true, false)] bool preloadAssemblies) + public void ClassLibraryMainLauncherRuns ([Values] bool preloadAssemblies, [Values] AndroidRuntime runtime) { + bool isRelease = runtime == AndroidRuntime.NativeAOT; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + + // TODO: NativeAOT currently dies with a Java android.os.DeadObjectException exception (GC issue?): + // + // Exception thrown during dispatchAppVisibility Window{74689fa u0 com.xamarin.classlibrarymainlauncherruns/com.xamarin.classlibrarymainlauncherruns.MainActivity EXITING} + // android.os.DeadObjectException + // at android.os.BinderProxy.transactNative(Native Method) + // at android.os.BinderProxy.transact(BinderProxy.java:592) + // at android.view.IWindow$Stub$Proxy.dispatchAppVisibility(IWindow.java:538) + // at com.android.server.wm.WindowState.sendAppVisibilityToClients(WindowState.java:3183) + // at com.android.server.wm.WindowContainer.sendAppVisibilityToClients(WindowContainer.java:1233) + // at com.android.server.wm.WindowToken.setClientVisible(WindowToken.java:394) + // at com.android.server.wm.ActivityRecord.commitVisibility(ActivityRecord.java:5546) + // at com.android.server.wm.Transition.finishTransition(Transition.java:1485) + // at com.android.server.wm.TransitionController.finishTransition(TransitionController.java:1048) + // at com.android.server.wm.WindowOrganizerController.finishTransition(WindowOrganizerController.java:514) + // at android.window.IWindowOrganizerController$Stub.onTransact(IWindowOrganizerController.java:270) + // at com.android.server.wm.WindowOrganizerController.onTransact(WindowOrganizerController.java:230) + // at android.os.Binder.execTransactInternal(Binder.java:1446) + // at android.os.Binder.execTransact(Binder.java:1385) + if (runtime == AndroidRuntime.NativeAOT) { + Assert.Ignore ("NativeAOT currently dies at startup with a Java android.os.DeadObjectException exception"); + } + SwitchUser (); var path = Path.Combine ("temp", TestName); var app = new XamarinAndroidApplicationProject { + IsRelease = isRelease, ProjectName = "MyApp", }; + app.SetRuntime (runtime); if (!TestEnvironment.CommercialBuildAvailable) { app.SetAndroidSupportedAbis (DeviceAbi); } @@ -134,36 +181,69 @@ public void ClassLibraryMainLauncherRuns ([Values (true, false)] bool preloadAss } } -#pragma warning disable 414 - static object [] DebuggerCustomAppTestCases = new object [] { - new object[] { - /* embedAssemblies */ true, - /* activityStarts */ true, - /* packageFormat */ "apk", - }, - new object[] { - /* embedAssemblies */ false, - /* activityStarts */ true, - /* packageFormat */ "apk", - }, - new object[] { - /* embedAssemblies */ true, - /* activityStarts */ true, - /* packageFormat */ "aab", - }, - new object[] { - /* embedAssemblies */ false, - /* activityStarts */ true, - /* packageFormat */ "aab", - }, - }; -#pragma warning restore 414 + static IEnumerable Get_CustomApplicationRunsWithDebuggerAndBreaks_Data () + { + var ret = new List (); + + foreach (AndroidRuntime runtime in Enum.GetValues (typeof (AndroidRuntime))) { + // TODO: once CoreCLR debugging works, this needs to be adjusted accordingly + if (runtime != AndroidRuntime.MonoVM) { + continue; + } + + AddTestData ( + embedAssemblies: true, + activityStarts: true, + packageFormat: "apk", + runtime: runtime + ); + + AddTestData ( + embedAssemblies: false, + activityStarts: true, + packageFormat: "apk", + runtime: runtime + ); + + AddTestData ( + embedAssemblies: true, + activityStarts: true, + packageFormat: "aab", + runtime: runtime + ); + + AddTestData ( + embedAssemblies: false, + activityStarts: true, + packageFormat: "aab", + runtime: runtime + ); + } + + return ret; + + void AddTestData (bool embedAssemblies, bool activityStarts, string packageFormat, AndroidRuntime runtime) + { + ret.Add (new object[] { + embedAssemblies, + activityStarts, + packageFormat, + runtime, + }); + } + } + // MonoVM-only test for the moment. [Test, Category ("Debugger")] - [TestCaseSource (nameof (DebuggerCustomAppTestCases))] + [TestCaseSource (nameof (Get_CustomApplicationRunsWithDebuggerAndBreaks_Data))] [Retry(5)] - public void CustomApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, bool activityStarts, string packageFormat) + public void CustomApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, bool activityStarts, string packageFormat, AndroidRuntime runtime) { + const bool isRelease = false; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + AssertCommercialBuild (); SwitchUser (); @@ -174,10 +254,10 @@ public void CustomApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, bo } var proj = new XamarinAndroidApplicationProject () { - IsRelease = false, + IsRelease = isRelease, }; - // MonoVM-only test - proj.SetRuntime (Android.Tasks.AndroidRuntime.MonoVM); + + proj.SetRuntime (runtime); proj.SetAndroidSupportedAbis (DeviceAbi); proj.SetProperty ("EmbedAssembliesIntoApk", embedAssemblies.ToString ()); proj.SetProperty ("AndroidPackageFormat", packageFormat); @@ -281,70 +361,114 @@ public override void OnCreate () } } -#pragma warning disable 414 - static object [] DebuggerTestCases = new object [] { - new object[] { - /* embedAssemblies */ true, - /* user */ null, - /* packageFormat */ "apk", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ true, - /* user */ null, - /* packageFormat */ "apk", - /* useLatestSdk */ false, - }, - new object[] { - /* embedAssemblies */ false, - /* user */ null, - /* packageFormat */ "apk", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ true, - /* user */ DeviceTest.GuestUserName, - /* packageFormat */ "apk", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ false, - /* user */ DeviceTest.GuestUserName, - /* packageFormat */ "apk", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ true, - /* user */ null, - /* packageFormat */ "aab", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ false, - /* user */ null, - /* packageFormat */ "aab", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ true, - /* user */ DeviceTest.GuestUserName, - /* packageFormat */ "aab", - /* useLatestSdk */ true, - }, - new object[] { - /* embedAssemblies */ false, - /* user */ DeviceTest.GuestUserName, - /* packageFormat */ "aab", - /* useLatestSdk */ true, - }, - }; -#pragma warning restore 414 + static IEnumerable Get_ApplicationRunsWithDebuggerAndBreaks_Data () + { + var ret = new List (); + + foreach (AndroidRuntime runtime in Enum.GetValues (typeof (AndroidRuntime))) { + // TODO: once CoreCLR debugging works, this needs to be adjusted accordingly + if (runtime != AndroidRuntime.MonoVM) { + continue; + } + + AddTestData ( + embedAssemblies: true, + username: null, + packageFormat: "apk", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: true, + username: null, + packageFormat: "apk", + useLatestSdk: false, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: false, + username: null, + packageFormat: "apk", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: true, + username: DeviceTest.GuestUserName, + packageFormat: "apk", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: false, + username: DeviceTest.GuestUserName, + packageFormat: "apk", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: true, + username: null, + packageFormat: "aab", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: false, + username: null, + packageFormat: "aab", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: true, + username: DeviceTest.GuestUserName, + packageFormat: "aab", + useLatestSdk: true, + runtime: runtime + ); + + AddTestData ( + embedAssemblies: false, + username: DeviceTest.GuestUserName, + packageFormat: "aab", + useLatestSdk: true, + runtime: runtime + ); + } + return ret; + + void AddTestData (bool embedAssemblies, string username, string packageFormat, bool useLatestSdk, AndroidRuntime runtime) + { + ret.Add (new object[] { + embedAssemblies, + username, + packageFormat, + useLatestSdk, + runtime, + }); + } + } + + // MonoVM-only test for the moment. [Test, Category ("Debugger"), Category ("WearOS")] - [TestCaseSource (nameof(DebuggerTestCases))] + [TestCaseSource (nameof(Get_ApplicationRunsWithDebuggerAndBreaks_Data))] [Retry (5)] - public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string username, string packageFormat, bool useLatestSdk) + public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string username, string packageFormat, bool useLatestSdk, AndroidRuntime runtime) { + const bool isRelease = false; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + AssertCommercialBuild (); SwitchUser (); WaitFor (5000); @@ -366,6 +490,7 @@ public void ApplicationRunsWithDebuggerAndBreaks (bool embedAssemblies, string u } var lib = new XamarinAndroidLibraryProject { + IsRelease = isRelease, ProjectName = "Library1", Sources = { new BuildItem.Source ("Foo.cs") { @@ -382,11 +507,11 @@ public Foo () var app = new XamarinFormsAndroidApplicationProject { ProjectName = "App", - IsRelease = false, + IsRelease = isRelease, EmbedAssembliesIntoApk = embedAssemblies, }; - // MonoVM-only test - app.SetRuntime (Android.Tasks.AndroidRuntime.MonoVM); + + app.SetRuntime (runtime); if (!useLatestSdk) { lib.TargetFramework = "net9.0-android"; app.TargetFramework = "net9.0-android";