diff --git a/build-tools/automation/yaml-templates/stage-package-tests.yaml b/build-tools/automation/yaml-templates/stage-package-tests.yaml index 869ac45cee1..47a7a9db5ed 100644 --- a/build-tools/automation/yaml-templates/stage-package-tests.yaml +++ b/build-tools/automation/yaml-templates/stage-package-tests.yaml @@ -203,6 +203,16 @@ stages: artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab artifactFolder: $(DotNetTargetFramework)-CoreCLR + - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml + parameters: + configuration: $(XA.Build.Configuration) + testName: Mono.Android.NET_Tests-CoreCLRTrimmable + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + testResultsFiles: TestResult-Mono.Android.NET_Tests-$(XA.Build.Configuration)CoreCLRTrimmable.xml + extraBuildArgs: -p:_AndroidTypeMapImplementation=trimmable -p:UseMonoRuntime=false + artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab + artifactFolder: $(DotNetTargetFramework)-CoreCLRTrimmable + - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml parameters: configuration: $(XA.Build.Configuration) diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs index b46f27b8ff3..c63fcaca525 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs @@ -132,9 +132,7 @@ static void EmitPeers (TypeMapAssemblyData model, string jniName, // Emit TypeMapAssociation for all proxy-backed types so managed → proxy // lookup works even when the final JNI name differs from the type's attributes. - // Generic definitions are included — their proxy types derive from the - // non-generic `JavaPeerProxy` base so the CLR can load them without - // resolving an open generic argument. + // Generic definitions are supported via the non-generic `JavaPeerProxy` base. var assocProxy = (i > 0 && primaryProxy != null) ? primaryProxy : proxy; if (assocProxy != null) { model.Associations.Add (new TypeMapAssociationData { diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs index 4970d59dc00..7ebb9340c26 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs @@ -11,6 +11,11 @@ public class TrimmableTypeMapGenerator { readonly ITrimmableTypeMapLogger logger; + static readonly HashSet RequiredFrameworkDeferredRegistrationTypes = new (StringComparer.Ordinal) { + "android/app/Application", + "android/app/Instrumentation", + }; + public TrimmableTypeMapGenerator (ITrimmableTypeMapLogger logger) { this.logger = logger ?? throw new ArgumentNullException (nameof (logger)); @@ -40,6 +45,7 @@ public TrimmableTypeMapResult Execute ( RootManifestReferencedTypes (allPeers, PrepareManifestForRooting (manifestTemplate, manifestConfig)); PropagateDeferredRegistrationToBaseClasses (allPeers); + PropagateCannotRegisterToDescendants (allPeers); var generatedAssemblies = GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion); var jcwPeers = allPeers.Where (p => @@ -48,14 +54,7 @@ public TrimmableTypeMapResult Execute ( logger.LogGeneratingJcwFilesInfo (jcwPeers.Count, allPeers.Count); var generatedJavaSources = GenerateJcwJavaSources (jcwPeers); - // Collect Application/Instrumentation types that need deferred registerNatives - var appRegTypes = allPeers - // Include all deferred-registration peers here: framework MCWs still need - // ApplicationRegistration.java even without generated ACWs, and abstract - // base types can own the native methods that derived types invoke. - .Where (p => p.CannotRegisterInStaticConstructor) - .Select (p => JniSignatureHelper.JniNameToJavaName (p.JavaName)) - .ToList (); + var appRegTypes = CollectApplicationRegistrationTypes (allPeers); if (appRegTypes.Count > 0) { logger.LogDeferredRegistrationTypesInfo (appRegTypes.Count); } @@ -67,6 +66,33 @@ public TrimmableTypeMapResult Execute ( return new TrimmableTypeMapResult (generatedAssemblies, generatedJavaSources, allPeers, manifest, appRegTypes); } + internal static List CollectApplicationRegistrationTypes (List allPeers) + { + var appRegTypes = new List (); + var seen = new HashSet (StringComparer.Ordinal); + + foreach (var peer in allPeers) { + if (!peer.CannotRegisterInStaticConstructor) { + continue; + } + + // ApplicationRegistration.java is compiled against the app's target Android API + // surface. Legacy framework descendants such as android.test.* may not exist there, + // so keep only the two framework roots plus app/runtime types that participate in + // the deferred-registration flow. + if (peer.DoNotGenerateAcw && !RequiredFrameworkDeferredRegistrationTypes.Contains (peer.JavaName)) { + continue; + } + + var javaName = JniSignatureHelper.JniNameToJavaName (peer.JavaName); + if (seen.Add (javaName)) { + appRegTypes.Add (javaName); + } + } + + return appRegTypes; + } + GeneratedManifest GenerateManifest (List allPeers, AssemblyManifestInfo assemblyManifestInfo, ManifestConfig config, XDocument? manifestTemplate) { @@ -217,8 +243,7 @@ internal void RootManifestReferencedTypes (List allPeers, XDocumen /// TestInstrumentation_1 must also defer — otherwise the base class <clinit> will call /// registerNatives before the managed runtime is ready. /// - internal static void PropagateDeferredRegistrationToBaseClasses (List allPeers) - { + internal static void PropagateDeferredRegistrationToBaseClasses (List allPeers) { // In practice only 1–2 types need propagation (one Application, maybe one // Instrumentation), each with a short base-class chain. A linear scan per // ancestor is simpler and cheaper than building a Dictionary> @@ -247,6 +272,43 @@ static void PropagateToAncestors (string? baseJniName, List allPee } } + /// + /// Propagates DOWN + /// from Application/Instrumentation types to all their descendants. Any subclass of + /// an Instrumentation/Application type can be loaded by Android before the native + /// library is ready, so it must also use the lazy __md_registerNatives pattern. + /// + internal static void PropagateCannotRegisterToDescendants (List allPeers) + { + // Build a set of JavaNames that have CannotRegisterInStaticConstructor + var cannotRegister = new HashSet (StringComparer.Ordinal); + foreach (var peer in allPeers) { + if (peer.CannotRegisterInStaticConstructor) { + cannotRegister.Add (peer.JavaName); + } + } + + // Also include the framework base types + cannotRegister.Add ("android/app/Application"); + cannotRegister.Add ("android/app/Instrumentation"); + + // Propagate to descendants: if your base is in the set, you're in the set too + bool changed = true; + while (changed) { + changed = false; + foreach (var peer in allPeers) { + if (peer.CannotRegisterInStaticConstructor || peer.BaseJavaName is null) { + continue; + } + if (cannotRegister.Contains (peer.BaseJavaName)) { + peer.CannotRegisterInStaticConstructor = true; + cannotRegister.Add (peer.JavaName); + changed = true; + } + } + } + } + static void AddPeerByDotName (Dictionary> peersByDotName, string dotName, JavaPeerInfo peer) { if (!peersByDotName.TryGetValue (dotName, out var list)) { diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets index 52ce49be5cf..b36c8e590f7 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets @@ -92,15 +92,35 @@ + + + + <_AdditionalNativeConfigResolvedAssemblies Remove="@(_AdditionalNativeConfigResolvedAssemblies)" /> + <_AdditionalNativeConfigResolvedAssemblies Include="$(_TypeMapOutputDirectory)*.dll" /> + + + @@ -112,28 +132,6 @@ - - - <_TypeMapFirstAbi Condition=" '$(AndroidSupportedAbis)' != '' ">$([System.String]::Copy('$(AndroidSupportedAbis)').Split(';')[0]) - <_TypeMapFirstAbi Condition=" '$(_TypeMapFirstAbi)' == '' ">arm64-v8a - <_TypeMapFirstRid Condition=" '$(_TypeMapFirstAbi)' == 'arm64-v8a' ">android-arm64 - <_TypeMapFirstRid Condition=" '$(_TypeMapFirstAbi)' == 'armeabi-v7a' ">android-arm - <_TypeMapFirstRid Condition=" '$(_TypeMapFirstAbi)' == 'x86_64' ">android-x64 - <_TypeMapFirstRid Condition=" '$(_TypeMapFirstAbi)' == 'x86' ">android-x86 - - - - - <_ResolvedAssemblies Include="$(_TypeMapOutputDirectory)*.dll"> - $(_TypeMapFirstAbi) - $(_TypeMapFirstRid) - $(_TypeMapFirstAbi)/%(Filename)%(Extension) - $(_TypeMapFirstAbi)/ - - - - diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs index 7b01f6f08e5..92aab238757 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs @@ -27,6 +27,8 @@ public class GenerateNativeApplicationConfigSources : AndroidTask [Required] public ITaskItem[] ResolvedAssemblies { get; set; } = []; + public ITaskItem[]? AdditionalResolvedAssemblies { get; set; } + public ITaskItem[]? NativeLibraries { get; set; } public ITaskItem[]? NativeLibrariesNoJniPreload { get; set; } public ITaskItem[]? NativeLibrariesAlwaysJniPreload { get; set; } @@ -202,6 +204,13 @@ public override bool RunTask () GetRequiredTokens (assembly.ItemSpec, out android_runtime_jnienv_class_token, out jnienv_initialize_method_token, out jnienv_registerjninatives_method_token); } + if (AdditionalResolvedAssemblies != null) { + foreach (ITaskItem assembly in AdditionalResolvedAssemblies) { + updateNameWidth (assembly); + updateAssemblyCount (assembly); + } + } + if (!UseAssemblyStore) { int abiNameLength = 0; foreach (string abi in SupportedAbis) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GenerateTrimmableTypeMapTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GenerateTrimmableTypeMapTests.cs index 096fd7d9f34..f5954e7e77d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GenerateTrimmableTypeMapTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GenerateTrimmableTypeMapTests.cs @@ -169,6 +169,8 @@ public void Execute_ManifestPlaceholdersAreResolvedForRooting () var registrationText = File.ReadAllText (applicationRegistration); StringAssert.Contains ("mono.android.Runtime.registerNatives (android.app.Application.class);", registrationText); StringAssert.Contains ("mono.android.Runtime.registerNatives (android.app.Instrumentation.class);", registrationText); + StringAssert.DoesNotContain ("android.test.InstrumentationTestRunner.class", registrationText); + StringAssert.DoesNotContain ("android.test.mock.MockApplication.class", registrationText); Assert.IsFalse (warnings.Any (w => w.Code == "XA4250"), "Resolved placeholder-based manifest references should not log XA4250."); } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs index 15938fd1439..70bbe93abe5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.cs @@ -1,4 +1,6 @@ using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using NUnit.Framework; using Xamarin.Android.Tasks; using Xamarin.ProjectTools; @@ -11,17 +13,15 @@ public class TrimmableTypeMapBuildTests : BaseTest { [Test] public void Build_WithTrimmableTypeMap_Succeeds () { - var proj = new XamarinAndroidApplicationProject (); + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; proj.SetRuntime (AndroidRuntime.CoreCLR); proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable"); - // Full Build will fail downstream (manifest generation not yet implemented for trimmable path), - // but _GenerateJavaStubs runs and completes before the failure point. using var builder = CreateApkBuilder (); - builder.ThrowOnBuildFailure = false; - builder.Build (proj); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); - // Verify _GenerateJavaStubs ran by checking typemap outputs exist var intermediateDir = builder.Output.GetIntermediaryPath ("typemap"); DirectoryAssert.Exists (intermediateDir); } @@ -29,27 +29,85 @@ public void Build_WithTrimmableTypeMap_Succeeds () [Test] public void Build_WithTrimmableTypeMap_IncrementalBuild () { - var proj = new XamarinAndroidApplicationProject (); + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; proj.SetRuntime (AndroidRuntime.CoreCLR); proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable"); - // Full Build will fail downstream (manifest generation not yet implemented for trimmable path), - // but _GenerateJavaStubs runs and completes before the failure point. using var builder = CreateApkBuilder (); - builder.ThrowOnBuildFailure = false; - builder.Build (proj); + Assert.IsTrue (builder.Build (proj), "First build should have succeeded."); - // Verify _GenerateJavaStubs ran on the first build var intermediateDir = builder.Output.GetIntermediaryPath ("typemap"); DirectoryAssert.Exists (intermediateDir); - // Second build with no changes — _GenerateJavaStubs should be skipped - builder.Build (proj); + Assert.IsTrue (builder.Build (proj), "Second build should have succeeded."); Assert.IsTrue ( builder.Output.IsTargetSkipped ("_GenerateJavaStubs"), "_GenerateJavaStubs should be skipped on incremental build."); } + [Test] + public void Build_WithTrimmableTypeMap_DoesNotHitCopyIfChangedMismatch () + { + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable"); + + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + + Assert.IsFalse ( + StringAssertEx.ContainsText (builder.LastBuildOutput, "source and destination count mismatch"), + $"{builder.BuildLogFile} should not fail with XACIC7004."); + Assert.IsFalse ( + StringAssertEx.ContainsText (builder.LastBuildOutput, "Internal error: architecture"), + $"{builder.BuildLogFile} should keep trimmable typemap assemblies aligned across ABIs."); + } + + [Test] + public void Build_WithTrimmableTypeMap_AssemblyStoreMappingsStayInRange () + { + var proj = new XamarinAndroidApplicationProject { + IsRelease = true, + }; + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty ("_AndroidTypeMapImplementation", "trimmable"); + + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + + var environmentFiles = Directory.GetFiles (builder.Output.GetIntermediaryPath ("android"), "environment.*.ll"); + Assert.IsNotEmpty (environmentFiles, "Expected generated environment..ll files."); + + foreach (var environmentFile in environmentFiles) { + var abi = Path.GetFileNameWithoutExtension (environmentFile).Substring ("environment.".Length); + var manifestFile = builder.Output.GetIntermediaryPath (Path.Combine ("app_shared_libraries", abi, "assembly-store.so.manifest")); + + if (!File.Exists (manifestFile)) { + continue; + } + + var environmentText = File.ReadAllText (environmentFile); + var runtimeDataMatch = Regex.Match (environmentText, @"assembly_store_bundled_assemblies.*\[(\d+)\s+x"); + Assert.IsTrue (runtimeDataMatch.Success, $"{environmentFile} should declare assembly_store_bundled_assemblies."); + + var runtimeDataCount = int.Parse (runtimeDataMatch.Groups [1].Value); + var maxMappingIndex = File.ReadLines (manifestFile) + .Select (line => Regex.Match (line, @"\bmi:(\d+)\b")) + .Where (match => match.Success) + .Select (match => int.Parse (match.Groups [1].Value)) + .Max (); + + Assert.That ( + runtimeDataCount, + Is.GreaterThan (maxMappingIndex), + $"{Path.GetFileName (environmentFile)} should allocate enough runtime slots for {Path.GetFileName (manifestFile)}."); + } + } + [Test] public void TrimmableTypeMap_PreserveList_IsPackagedInSdk () { diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 1f37f9198bc..dcd9f99a614 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1727,6 +1727,7 @@ because xbuild doesn't support framework reference assemblies. { + new JavaPeerInfo { + JavaName = "android/app/Application", CompatJniName = "android.app.Application", + ManagedTypeName = "Android.App.Application", ManagedTypeNamespace = "Android.App", ManagedTypeShortName = "Application", + AssemblyName = "Mono.Android", DoNotGenerateAcw = true, CannotRegisterInStaticConstructor = true, + }, + new JavaPeerInfo { + JavaName = "android/app/Instrumentation", CompatJniName = "android.app.Instrumentation", + ManagedTypeName = "Android.App.Instrumentation", ManagedTypeNamespace = "Android.App", ManagedTypeShortName = "Instrumentation", + AssemblyName = "Mono.Android", DoNotGenerateAcw = true, CannotRegisterInStaticConstructor = true, + }, + new JavaPeerInfo { + JavaName = "android/test/InstrumentationTestRunner", CompatJniName = "android.test.InstrumentationTestRunner", + ManagedTypeName = "Android.Test.InstrumentationTestRunner", ManagedTypeNamespace = "Android.Test", ManagedTypeShortName = "InstrumentationTestRunner", + AssemblyName = "Mono.Android", BaseJavaName = "android/app/Instrumentation", DoNotGenerateAcw = true, CannotRegisterInStaticConstructor = true, + }, + new JavaPeerInfo { + JavaName = "android/test/mock/MockApplication", CompatJniName = "android.test.mock.MockApplication", + ManagedTypeName = "Android.Test.Mock.MockApplication", ManagedTypeNamespace = "Android.Test.Mock", ManagedTypeShortName = "MockApplication", + AssemblyName = "Mono.Android", BaseJavaName = "android/app/Application", DoNotGenerateAcw = true, CannotRegisterInStaticConstructor = true, + }, + new JavaPeerInfo { + JavaName = "my/app/BaseInstrumentation", CompatJniName = "my.app.BaseInstrumentation", + ManagedTypeName = "My.App.BaseInstrumentation", ManagedTypeNamespace = "My.App", ManagedTypeShortName = "BaseInstrumentation", + AssemblyName = "MyApp", IsAbstract = true, CannotRegisterInStaticConstructor = true, + }, + new JavaPeerInfo { + JavaName = "my/app/MyInstrumentation", CompatJniName = "my.app.MyInstrumentation", + ManagedTypeName = "My.App.MyInstrumentation", ManagedTypeNamespace = "My.App", ManagedTypeShortName = "MyInstrumentation", + AssemblyName = "MyApp", BaseJavaName = "my/app/BaseInstrumentation", CannotRegisterInStaticConstructor = true, + }, + }; + + var types = TrimmableTypeMapGenerator.CollectApplicationRegistrationTypes (peers); + + Assert.Contains ("android.app.Application", types); + Assert.Contains ("android.app.Instrumentation", types); + Assert.Contains ("my.app.BaseInstrumentation", types); + Assert.Contains ("my.app.MyInstrumentation", types); + Assert.DoesNotContain ("android.test.InstrumentationTestRunner", types); + Assert.DoesNotContain ("android.test.mock.MockApplication", types); + } + [Fact] public void Execute_NullAssemblyList_Throws () { diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs index 1b13316cc3d..2dd99972435 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JavaObjectExtensionsTests.cs @@ -15,7 +15,7 @@ namespace Java.InteropTests { [TestFixture] public class JavaObjectExtensionsTests { - [Test] + [Test, Category ("TrimmableIgnore")] public void JavaCast_BaseToGenericWrapper () { using (var list = new JavaList (new[]{ 1, 2, 3 })) @@ -41,7 +41,7 @@ public void JavaCast_InterfaceCast () } } - [Test] + [Test, Category ("TrimmableIgnore")] public void JavaCast_BadInterfaceCast () { using var n = new Java.Lang.Integer (42); @@ -67,7 +67,7 @@ public void JavaCast_ObtainOriginalInstance () Assert.AreSame (list, al); } - [Test] + [Test, Category ("TrimmableIgnore")] public void JavaCast_InvalidTypeCastThrows () { using (var s = new Java.Lang.String ("value")) { @@ -75,7 +75,7 @@ public void JavaCast_InvalidTypeCastThrows () } } - [Test] + [Test, Category ("TrimmableIgnore")] public void JavaCast_CheckForManagedSubclasses () { using (var o = CreateObject ()) { @@ -83,7 +83,7 @@ public void JavaCast_CheckForManagedSubclasses () } } - [Test] + [Test, Category ("TrimmableIgnore")] public void JavaAs () { using var v = new Java.InteropTests.MyJavaInterfaceImpl (); diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs index a563f56fd3a..be5347a780a 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs @@ -121,7 +121,7 @@ public void InvokingNullInstanceDoesNotCrashDalvik () } } - [Test] + [Test, Category ("TrimmableIgnore")] public void NewOpenGenericTypeThrows () { try { @@ -301,7 +301,7 @@ public void ActivatedDirectObjectSubclassesShouldBeRegistered () } } - [Test] + [Test, Category ("TrimmableIgnore")] public void ActivatedDirectThrowableSubclassesShouldBeRegistered () { if (Build.VERSION.SdkInt <= BuildVersionCodes.GingerbreadMr1) diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj index 33bb45ed65c..2ae2afb3233 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj @@ -37,6 +37,12 @@ $(ExcludeCategories):InetAccess:NetworkInterfaces + + false + CoreCLRTrimmable + $(ExcludeCategories):NativeTypeMap:TrimmableIgnore + + @@ -68,8 +74,17 @@ + + + + + + + <_AndroidRemapMembers Include="Remaps.xml" /> <_AndroidRemapMembers Include="IsAssignableFromRemaps.xml" Condition=" '$(_AndroidIsAssignableFromCheck)' == 'false' " /> diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/TrimmerRoots.xml b/tests/Mono.Android-Tests/Mono.Android-Tests/TrimmerRoots.xml new file mode 100644 index 00000000000..8197e4f5994 --- /dev/null +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/TrimmerRoots.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs index ddc9697bb43..6f59ecf91da 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs @@ -136,7 +136,7 @@ public async Task DoesNotDisposeContentStream() listener.Close (); } - [Test] + [Test, Category ("TrimmableIgnore")] public async Task ServerCertificateCustomValidationCallback_ApproveRequest () { bool callbackHasBeenCalled = false; @@ -162,7 +162,7 @@ public async Task ServerCertificateCustomValidationCallback_ApproveRequest () Assert.IsTrue (callbackHasBeenCalled, "custom validation callback hasn't been called"); } - [Test] + [Test, Category ("TrimmableIgnore")] public async Task ServerCertificateCustomValidationCallback_RejectRequest () { bool callbackHasBeenCalled = false; @@ -180,7 +180,7 @@ public async Task ServerCertificateCustomValidationCallback_RejectRequest () Assert.IsTrue (callbackHasBeenCalled, "custom validation callback hasn't been called"); } - [Test] + [Test, Category ("TrimmableIgnore")] public async Task ServerCertificateCustomValidationCallback_ApprovesRequestWithInvalidCertificate () { bool callbackHasBeenCalled = false; @@ -207,7 +207,7 @@ public async Task NoServerCertificateCustomValidationCallback_ThrowsWhenThereIsC await AssertRejectsRemoteCertificate (() => client.GetStringAsync ("https://wrong.host.badssl.com/")); } - [Test] + [Test, Category ("TrimmableIgnore")] public async Task ServerCertificateCustomValidationCallback_IgnoresCertificateHostnameMismatch () { bool callbackHasBeenCalled = false; @@ -228,7 +228,7 @@ public async Task ServerCertificateCustomValidationCallback_IgnoresCertificateHo Assert.AreEqual (SslPolicyErrors.RemoteCertificateNameMismatch, reportedErrors & SslPolicyErrors.RemoteCertificateNameMismatch); } - [Test] + [Test, Category ("TrimmableIgnore")] public async Task ServerCertificateCustomValidationCallback_Redirects () { int callbackCounter = 0; diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs index 215fde081a9..42f4fa0c671 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs @@ -25,6 +25,32 @@ protected override string LogTag protected NUnitInstrumentation(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer) { + if (Microsoft.Android.Runtime.RuntimeFeature.TrimmableTypeMap) { + // Java.Interop-Tests fixtures that use JavaObject types (not Java.Lang.Object) + // still need JCW Java classes or Java-side support that the trimmable typemap + // path does not emit yet. + // NOTE: Tests in this project that are trimmable-incompatible use + // [Category("TrimmableIgnore")] so they can be excluded via ExcludeCategories in + // the .csproj instead. Only tests from the external Java.Interop-Tests assembly + // (which we don't control) need to be listed here by name. + ExcludedTestNames = new [] { + "Java.InteropTests.JavaObjectTest", + "Java.InteropTests.InvokeVirtualFromConstructorTests", + "Java.InteropTests.JniPeerMembersTests", + "Java.InteropTests.JniTypeManagerTests", + "Java.InteropTests.JniValueMarshaler_object_ContractTests", + "Java.InteropTests.JavaExceptionTests.InnerExceptionIsNotAProxy", + + // JavaCast/JavaAs interface resolution still differs under trimmable typemap. + "Java.InteropTests.JavaPeerableExtensionsTests.JavaAs", + "Java.InteropTests.JavaPeerableExtensionsTests.JavaAs_Exceptions", + "Java.InteropTests.JavaPeerableExtensionsTests.JavaAs_InstanceThatDoesNotImplementInterfaceReturnsNull", + + // JavaObjectArray contract tests still need generic container factory support. + "Java.InteropTests.JavaObjectArray_object_ContractTest", + }; + + } } protected override IList GetTestAssemblies() @@ -39,4 +65,4 @@ protected override IList GetTestAssemblies() }; } } -} \ No newline at end of file +}