diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestDocumentTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestDocumentTest.cs new file mode 100644 index 00000000000..b4bb144001a --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestDocumentTest.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using Java.Interop.Tools.Cecil; +using Microsoft.Build.Utilities; +using Mono.Cecil; +using NUnit.Framework; +using Xamarin.Android.Tasks; + +namespace Xamarin.Android.Build.Tests; + +/// +/// In-process tests for how merges the computed +/// minSdkVersion/targetSdkVersion into a user-authored <uses-sdk/> element. +/// +[TestFixture] +[Parallelizable (ParallelScope.Self)] +public class ManifestDocumentTest : BaseTest +{ + const string DefaultMinSdkVersion = "21"; + const string DefaultTargetSdkVersion = "37"; + + static readonly XNamespace AndroidNs = "http://schemas.android.com/apk/res/android"; + + static string CreateManifest (string usesSdk) => $@" + + {usesSdk} + + +"; + + static XElement MergeAndGetUsesSdk (string usesSdk, out XDocument document) + { + var templateFile = Path.GetTempFileName (); + try { + File.WriteAllText (templateFile, CreateManifest (usesSdk)); + + var manifest = new ManifestDocument (templateFile) { + PackageName = "com.xamarin.usessdk", + TargetSdkVersion = DefaultTargetSdkVersion, + MinSdkVersion = DefaultMinSdkVersion, + VersionResolver = new MockVersionResolver { + GetApiLevelFromIdFunc = id => int.TryParse (id, out var apiLevel) ? (int?) apiLevel : null, + GetIdFromApiLevelFunc = apiLevel => apiLevel, + }, + }; + + var engine = new MockBuildEngine (TestContext.Out); + var log = new TaskLoggingHelper (engine, nameof (ManifestDocumentTest)); + manifest.Merge (log, new TypeDefinitionCache (), new List (), applicationClass: null, embed: false, bundledWearApplicationName: null, mergedManifestDocuments: null); + + var writer = new StringWriter (); + manifest.Save ((code, message) => { }, writer); + + document = XDocument.Parse (writer.ToString ()); + var element = document.Root.Element ("uses-sdk"); + Assert.IsNotNull (element, "uses-sdk element should exist"); + return element; + } finally { + File.Delete (templateFile); + } + } + + static void AssertUsesSdk (string usesSdk, string expectedMinSdkVersion, string expectedTargetSdkVersion) + { + var element = MergeAndGetUsesSdk (usesSdk, out var document); + + Assert.AreEqual (expectedMinSdkVersion, element.Attribute (AndroidNs + "minSdkVersion")?.Value, "unexpected android:minSdkVersion"); + Assert.AreEqual (expectedTargetSdkVersion, element.Attribute (AndroidNs + "targetSdkVersion")?.Value, "unexpected android:targetSdkVersion"); + + foreach (var comment in document.DescendantNodes ().OfType ()) { + StringAssert.DoesNotContain ("UsesMinSdkAttributes", comment.Value, "the lint suppression comment should no longer be emitted"); + } + } + + [Test] + public void NoUsesSdkElement () + { + AssertUsesSdk ("", DefaultMinSdkVersion, DefaultTargetSdkVersion); + } + + [Test] + public void MinSdkVersionOnly () + { + // Regression test: targetSdkVersion was never written, so `aapt2` defaulted it to minSdkVersion. + AssertUsesSdk (@"", "24", DefaultTargetSdkVersion); + } + + [Test] + public void TargetSdkVersionOnly () + { + AssertUsesSdk (@"", DefaultMinSdkVersion, "30"); + } + + [Test] + public void MinAndTargetSdkVersion () + { + AssertUsesSdk (@"", "24", "30"); + } +} + +class MockVersionResolver : IVersionResolver +{ + public Func GetApiLevelFromIdFunc { get; set; } = _ => 99; + public Func GetIdFromApiLevelFunc { get; set; } = _ => "API-99"; + + public int? GetApiLevelFromId (string id) => GetApiLevelFromIdFunc (id); + + public string GetIdFromApiLevel (string apiLevel) => GetIdFromApiLevelFunc (apiLevel); +} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs index 122208b3356..0f5d6141d80 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/ManifestTest.cs @@ -1728,16 +1728,6 @@ public void TargetSdkVersion_361In_36Out () File.Delete (templateFile); } } - - class MockVersionResolver : IVersionResolver - { - public Func GetApiLevelFromIdFunc { get; set; } = _ => 99; - public Func GetIdFromApiLevelFunc { get; set; } = _ => "API-99"; - - public int? GetApiLevelFromId (string id) => GetApiLevelFromIdFunc (id); - - public string GetIdFromApiLevel (string apiLevel) => GetIdFromApiLevelFunc (apiLevel); - } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs index ba9b5b878f8..5b040d4a458 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManifestDocument.cs @@ -324,13 +324,14 @@ public IList Merge (TaskLoggingHelper log, TypeDefinitionCache cache, Li uses.SetAttributeValue (androidNs + "minSdkVersion", minSdkVersionString); } + // If no targetSdkVersion is specified, set it. Otherwise `aapt2` defaults it to minSdkVersion. string targetSdkVersion; var tsv = uses.Attribute (androidNs + "targetSdkVersion"); if (tsv != null) targetSdkVersion = tsv.Value; else { targetSdkVersion = TargetSdkVersionName; - uses.AddBeforeSelf (new XComment ("suppress UsesMinSdkAttributes")); + uses.SetAttributeValue (androidNs + "targetSdkVersion", targetSdkVersion); } int? tryTargetSdkVersion = VersionResolver.GetApiLevelFromId (targetSdkVersion);