public void GetDependencyWhenBuildToolsAreMissingTest() { var apis = new ApiInfo [] { }; var path = Path.Combine("temp", TestName); var androidSdkPath = CreateFauxAndroidSdkDirectory(Path.Combine(path, "android-sdk"), null, apis); var referencesPath = CreateFauxReferencesDirectory(Path.Combine(path, "xbuild-frameworks"), apis); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, TargetFrameworkVersion = "v8.0", TargetSdkVersion = "26", UseLatestPlatformSdk = false, }; var parameters = new string [] { $"TargetFrameworkRootPath={referencesPath}", $"AndroidSdkDirectory={androidSdkPath}", }; string buildToolsVersion = GetExpectedBuildToolsVersion(); using (var builder = CreateApkBuilder(Path.Combine(path, proj.ProjectName), cleanupAfterSuccessfulBuild: false, cleanupOnDispose: false)) { builder.ThrowOnBuildFailure = false; builder.Target = "GetAndroidDependencies"; Assert.True(builder.Build(proj, parameters: parameters), string.Format("First Build should have succeeded")); int apiLevel = Builder.UseDotNet ? AndroidSdkResolver.GetMaxInstalledPlatform() : 26; StringAssertEx.Contains($"platforms/android-{apiLevel}", builder.LastBuildOutput, $"platforms/android-{apiLevel} should be a dependency."); StringAssertEx.Contains($"build-tools/{buildToolsVersion}", builder.LastBuildOutput, $"build-tools/{buildToolsVersion} should be a dependency."); StringAssertEx.Contains("platform-tools", builder.LastBuildOutput, "platform-tools should be a dependency."); } }
public void CheckClassesDexIsIncluded([Values("dx", "d8", "invalid")] string dexTool) { var proj = new XamarinAndroidApplicationProject() { IsRelease = true, DexTool = dexTool, }; using (var b = CreateApkBuilder()) { b.ThrowOnBuildFailure = false; if (Builder.UseDotNet && dexTool == "dx") { Assert.IsFalse(b.Build(proj), "build failed"); StringAssertEx.Contains("XA1023", b.LastBuildOutput, "Output should contain XA1023 errors"); return; } Assert.IsTrue(b.Build(proj), "build failed"); var apk = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", "UnnamedProject.UnnamedProject.apk"); using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.ContainsEntry("classes.dex"), "Apk should contain classes.dex"); } } }
public void Queries_API30([Values(true, false)] bool useAapt2) { if (!CommercialBuildAvailable) { Assert.Ignore("$(AndroidUseSharedRuntime) is required for this test."); return; } var proj = new XamarinAndroidApplicationProject { AndroidUseSharedRuntime = true, EmbedAssembliesIntoApk = false, }; proj.SetProperty("AndroidUseAapt2", useAapt2.ToString()); proj.AndroidManifest = proj.AndroidManifest.Replace("<uses-sdk />", "<uses-sdk android:targetSdkVersion=\"30\" />"); using (var b = CreateApkBuilder()) { Assert.IsTrue(b.Build(proj), "Build should have succeeded"); string manifest = b.Output.GetIntermediaryAsText(Path.Combine("android", "AndroidManifest.xml")); var doc = XDocument.Parse(manifest); var ns = XNamespace.Get("http://schemas.android.com/apk/res/android"); var names = doc.Element("manifest")? .Element("queries")? .Elements("package")? .Select(e => e.Attribute(ns + "name")?.Value); StringAssertEx.Contains("Mono.Android.DebugRuntime", names); StringAssertEx.Contains("Mono.Android.Platform.ApiLevel_30", names); } }
public void Bug12935([Values(true, false)] bool useAapt2) { AssertAaptSupported(useAapt2); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; proj.MainActivity = ScreenOrientationActivity; proj.AndroidUseAapt2 = useAapt2; var directory = $"temp/Bug12935_{useAapt2}"; using (var builder = CreateApkBuilder(directory)) { proj.TargetFrameworkVersion = "v4.2"; proj.AndroidManifest = string.Format(TargetSdkManifest, "17"); Assert.IsTrue(builder.Build(proj), "Build for TargetFrameworkVersion 17 should have succeeded"); var manifestFile = Path.Combine(Root, builder.ProjectDirectory, proj.IntermediateOutputPath, "android", "AndroidManifest.xml"); XDocument doc = XDocument.Load(manifestFile); var ns = doc.Root.GetNamespaceOfPrefix("android"); var screenOrientationXName = XName.Get("screenOrientation", ns.NamespaceName); var targetSdkXName = XName.Get("targetSdkVersion", ns.NamespaceName); var usesSdk = doc.XPathSelectElement("/manifest/uses-sdk"); Assert.IsNotNull(usesSdk, "Failed to read the uses-sdk element"); var targetSdk = usesSdk.Attribute(targetSdkXName); Assert.AreEqual("17", targetSdk.Value, "targetSdkVersion should have been 17"); var activityElement = doc.XPathSelectElement("/manifest/application/activity"); Assert.IsNotNull(activityElement, "Failed to read the activity element"); var screenOrientation = activityElement.Attribute(screenOrientationXName); Assert.IsNotNull(screenOrientation, "activity element did not contain a android:screenOrientation attribute"); Assert.AreEqual("sensorPortrait", screenOrientation.Value, "screenOrientation should have been sensorPortrait"); builder.Cleanup(); proj.TargetFrameworkVersion = "v4.1"; proj.AndroidManifest = string.Format(TargetSdkManifest, "16"); Assert.IsTrue(builder.Build(proj), "Build for TargetFrameworkVersion 16 should have succeeded"); doc = XDocument.Load(manifestFile); usesSdk = doc.XPathSelectElement("/manifest/uses-sdk"); Assert.IsNotNull(usesSdk, "Failed to read the uses-sdk element"); targetSdk = usesSdk.Attribute(targetSdkXName); Assert.AreEqual("16", targetSdk.Value, "targetSdkVersion should have been 16"); activityElement = doc.XPathSelectElement("/manifest/application/activity"); Assert.IsNotNull(activityElement, "Failed to read the activity element"); screenOrientation = activityElement.Attribute(screenOrientationXName); Assert.AreEqual("sensorPortrait", screenOrientation.Value, "screenOrientation for targetSdkVersion 16 should have been sensorPortrait"); builder.Cleanup(); builder.ThrowOnBuildFailure = false; proj.TargetFrameworkVersion = "v4.0.3"; proj.AndroidManifest = string.Format(TargetSdkManifest, "15"); Assert.IsFalse(builder.Build(proj), "Build for TargetFrameworkVersion 15 should have failed"); StringAssertEx.Contains(useAapt2 ? "APT2259: " : "APT1134: ", builder.LastBuildOutput); StringAssertEx.Contains(useAapt2 ? "APT2067" : "", builder.LastBuildOutput); StringAssertEx.Contains(Path.Combine("Properties", "AndroidManifest.xml"), builder.LastBuildOutput); StringAssertEx.Contains($"{(useAapt2 ? "2" : "1")} Error(s)", builder.LastBuildOutput); } }
public void BundledWearApp() { var target = "_UpdateAndroidResgen"; var path = Path.Combine("temp", TestName); var app = new XamarinAndroidApplicationProject { ProjectName = "MyApp", AndroidUseSharedRuntime = false, EmbedAssembliesIntoApk = true, }; var wear = new XamarinAndroidWearApplicationProject { AndroidUseSharedRuntime = false, EmbedAssembliesIntoApk = true, }; app.References.Add(new BuildItem.ProjectReference($"..\\{wear.ProjectName}\\{wear.ProjectName}.csproj", wear.ProjectName, wear.ProjectGuid) { MetadataValues = "IsAppExtension=True" }); using (var wearBuilder = CreateDllBuilder(Path.Combine(path, wear.ProjectName))) using (var appBuilder = CreateApkBuilder(Path.Combine(path, app.ProjectName))) { Assert.IsTrue(wearBuilder.Build(wear), "first wear build should have succeeded."); // In .NET 5+, just check for a build error if (Builder.UseDotNet) { appBuilder.ThrowOnBuildFailure = false; Assert.IsFalse(appBuilder.Build(app), "'dotnet' app build should have failed."); StringAssertEx.Contains($"error XA4312", appBuilder.LastBuildOutput, "Error should be XA4312"); return; } Assert.IsTrue(appBuilder.Build(app), "first app build should have succeeded."); StringAssertEx.Contains($"warning XA4312", appBuilder.LastBuildOutput, "Warning should be XA4312"); // Build with no changes Assert.IsTrue(wearBuilder.Build(wear, doNotCleanupOnUpdate: true), "second wear build should have succeeded."); Assert.IsTrue(wearBuilder.Output.IsTargetSkipped(target), $"`{target}` in wear build should be skipped!"); Assert.IsTrue(appBuilder.Build(app, doNotCleanupOnUpdate: true), "second app build should have succeeded."); Assert.IsTrue(appBuilder.LastBuildOutput.ContainsOccurances($"Skipping target \"{target}\"", 2), $"`{target}` in app build should be skipped!"); // Check the APK for the special Android Wear files var files = new [] { "res/raw/wearable_app.apk", "res/xml/wearable_app_desc.xml" }; var apk = Path.Combine(Root, appBuilder.ProjectDirectory, app.OutputPath, $"{app.PackageName}.apk"); FileAssert.Exists(apk); using (var zipFile = ZipHelper.OpenZip(apk)) { foreach (var file in files) { Assert.IsTrue(zipFile.ContainsEntry(file, caseSensitive: true), $"{file} should be in the apk!"); } } } }
public void GetDependencyNdkRequiredConditions(string property, bool ndkRequired) { var proj = new XamarinAndroidApplicationProject(); proj.AotAssemblies = true; proj.SetProperty(property, "true"); using (var builder = CreateApkBuilder()) { builder.Target = "GetAndroidDependencies"; Assert.IsTrue(builder.Build(proj), "Build should have succeeded."); IEnumerable <string> taskOutput = builder.LastBuildOutput .Select(x => x.Trim()) .SkipWhile(x => !x.StartsWith("Task \"CalculateProjectDependencies\"")) .SkipWhile(x => !x.StartsWith("Output Item(s):")) .TakeWhile(x => !x.StartsWith("Done executing task \"CalculateProjectDependencies\"")); if (ndkRequired) { StringAssertEx.Contains("ndk-bundle", taskOutput, "ndk-bundle should be a dependency."); } else { StringAssertEx.DoesNotContain("ndk-bundle", taskOutput, "ndk-bundle should not be a dependency."); } } }
public void TestAndroidStoreKey(bool useApkSigner, bool isRelease, string packageFormat, string androidKeyStore, string password, string expected, bool shouldInstall) { AssertHasDevices(); if (DeviceSdkVersion >= 30 && !useApkSigner && packageFormat == "apk") { Assert.Ignore($"Test Skipped. jarsigner and {packageFormat} does not work with API 30 and above"); return; } string path = Path.Combine("temp", TestName.Replace(expected, expected.Replace("-", "_"))); string storepassfile = Path.Combine(Root, path, "storepass.txt"); string keypassfile = Path.Combine(Root, path, "keypass.txt"); byte [] data = GetKeystore(); var proj = new XamarinAndroidApplicationProject() { IsRelease = isRelease }; Dictionary <string, string> envVar = new Dictionary <string, string> (); if (password.StartsWith("env:", StringComparison.Ordinal)) { envVar.Add("_MYPASSWORD", password.Replace("env:", string.Empty)); proj.SetProperty("AndroidSigningStorePass", "env:_MYPASSWORD"); proj.SetProperty("AndroidSigningKeyPass", "env:_MYPASSWORD"); } else if (password.StartsWith("file:", StringComparison.Ordinal)) { proj.SetProperty("AndroidSigningStorePass", $"file:{storepassfile}"); proj.SetProperty("AndroidSigningKeyPass", $"file:{keypassfile}"); } else { proj.SetProperty("AndroidSigningStorePass", password); proj.SetProperty("AndroidSigningKeyPass", password); } proj.SetAndroidSupportedAbis("armeabi-v7a", "x86"); proj.SetProperty("AndroidKeyStore", androidKeyStore); proj.SetProperty("AndroidSigningKeyStore", "test.keystore"); proj.SetProperty("AndroidSigningKeyAlias", "mykey"); proj.SetProperty("AndroidPackageFormat", packageFormat); proj.SetProperty("AndroidUseApkSigner", useApkSigner.ToString()); proj.OtherBuildItems.Add(new BuildItem(BuildActions.None, "test.keystore") { BinaryContent = () => data }); proj.OtherBuildItems.Add(new BuildItem(BuildActions.None, "storepass.txt") { TextContent = () => password.Replace("file:", string.Empty), Encoding = Encoding.ASCII, }); proj.OtherBuildItems.Add(new BuildItem(BuildActions.None, "keypass.txt") { TextContent = () => password.Replace("file:", string.Empty), Encoding = Encoding.ASCII, }); using (var b = CreateApkBuilder(path, false, false)) { b.ThrowOnBuildFailure = false; Assert.IsTrue(b.Build(proj, environmentVariables: envVar), "Build should have succeeded."); if (packageFormat == "apk") { StringAssertEx.Contains(expected, b.LastBuildOutput, "The Wrong keystore was used to sign the apk"); } b.BuildLogFile = "install.log"; Assert.AreEqual(shouldInstall, b.Install(proj, doNotCleanupOnUpdate: true), $"Install should have {(shouldInstall ? "succeeded" : "failed")}."); if (packageFormat == "aab") { StringAssertEx.Contains(expected, b.LastBuildOutput, "The Wrong keystore was used to sign the apk"); } if (!shouldInstall) { return; } b.BuildLogFile = "uninstall.log"; Assert.IsTrue(b.Uninstall(proj, doNotCleanupOnUpdate: true), "Uninstall should have succeeded."); } }
public void TestAndroidStoreKey(bool useApkSigner, bool isRelease, string packageFormat, string androidKeyStore, string password, string expected, bool shouldInstall) { if (!HasDevices) { Assert.Ignore("Test Skipped no devices or emulators found."); } string path = Path.Combine("temp", TestName.Replace(expected, expected.Replace("-", "_"))); string storepassfile = Path.Combine(Root, path, "storepass.txt"); string keypassfile = Path.Combine(Root, path, "keypass.txt"); byte [] data; using (var stream = typeof(XamarinAndroidCommonProject).Assembly.GetManifestResourceStream("Xamarin.ProjectTools.Resources.Base.test.keystore")) { data = new byte [stream.Length]; stream.Read(data, 0, (int)stream.Length); } var proj = new XamarinAndroidApplicationProject() { IsRelease = isRelease }; Dictionary <string, string> envVar = new Dictionary <string, string> (); if (password.StartsWith("env:", StringComparison.Ordinal)) { envVar.Add("_MYPASSWORD", password.Replace("env:", string.Empty)); proj.SetProperty("AndroidSigningStorePass", "env:_MYPASSWORD"); proj.SetProperty("AndroidSigningKeyPass", "env:_MYPASSWORD"); } else if (password.StartsWith("file:", StringComparison.Ordinal)) { proj.SetProperty("AndroidSigningStorePass", $"file:{storepassfile}"); proj.SetProperty("AndroidSigningKeyPass", $"file:{keypassfile}"); } else { proj.SetProperty("AndroidSigningStorePass", password); proj.SetProperty("AndroidSigningKeyPass", password); } var abis = new string [] { "armeabi-v7a", "x86" }; proj.SetProperty(KnownProperties.AndroidSupportedAbis, string.Join(";", abis)); proj.SetProperty("AndroidKeyStore", androidKeyStore); proj.SetProperty("AndroidSigningKeyStore", "test.keystore"); proj.SetProperty("AndroidSigningKeyAlias", "mykey"); proj.SetProperty("AndroidPackageFormat", packageFormat); proj.SetProperty("AndroidUseApkSigner", useApkSigner.ToString()); proj.OtherBuildItems.Add(new BuildItem(BuildActions.None, "test.keystore") { BinaryContent = () => data }); proj.OtherBuildItems.Add(new BuildItem(BuildActions.None, "storepass.txt") { TextContent = () => password.Replace("file:", string.Empty), Encoding = Encoding.ASCII, }); proj.OtherBuildItems.Add(new BuildItem(BuildActions.None, "keypass.txt") { TextContent = () => password.Replace("file:", string.Empty), Encoding = Encoding.ASCII, }); using (var b = CreateApkBuilder(path, false, false)) { b.Verbosity = LoggerVerbosity.Diagnostic; b.ThrowOnBuildFailure = false; Assert.IsTrue(b.Build(proj, environmentVariables: envVar), "Build should have succeeded."); if (packageFormat == "apk") { StringAssertEx.Contains(expected, b.LastBuildOutput, "The Wrong keystore was used to sign the apk"); } b.BuildLogFile = "install.log"; Assert.AreEqual(shouldInstall, b.Install(proj, doNotCleanupOnUpdate: true), $"Install should have {(shouldInstall ? "succeeded" : "failed")}."); if (packageFormat == "aab") { StringAssertEx.Contains(expected, b.LastBuildOutput, "The Wrong keystore was used to sign the apk"); } if (!shouldInstall) { return; } b.BuildLogFile = "uninstall.log"; Assert.IsTrue(b.Uninstall(proj, doNotCleanupOnUpdate: true), "Uninstall should have succeeded."); } }