public void CheckHttpClientHandlerType() { var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; var httpClientHandlerVarName = "XA_HTTP_CLIENT_HANDLER_TYPE"; var expectedDefaultValue = "System.Net.Http.HttpClientHandler, System.Net.Http"; var expectedUpdatedValue = "Xamarin.Android.Net.AndroidClientHandler"; var supportedAbis = "armeabi-v7a;arm64-v8a"; proj.SetAndroidSupportedAbis(supportedAbis); using (var b = CreateApkBuilder()) { proj.SetProperty("AndroidHttpClientHandlerType", expectedDefaultValue); Assert.IsTrue(b.Build(proj), "Build should have succeeded."); var intermediateOutputDir = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath); List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); Dictionary <string, string> envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.ContainsKey(httpClientHandlerVarName), $"Environment should contain '{httpClientHandlerVarName}'."); Assert.AreEqual(expectedDefaultValue, envvars[httpClientHandlerVarName]); proj.SetProperty("AndroidHttpClientHandlerType", expectedUpdatedValue); Assert.IsTrue(b.Build(proj), "Second build should have succeeded."); envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.ContainsKey(httpClientHandlerVarName), $"Environment should contain '{httpClientHandlerVarName}'."); Assert.AreEqual(expectedUpdatedValue, envvars[httpClientHandlerVarName]); } }
public void CheckMonoDebugIsAddedToEnvironment([Values("", "Normal", "Offline")] string sequencePointsMode) { const string supportedAbis = "armeabi-v7a;x86"; var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; proj.SetProperty("_AndroidSequencePointsMode", sequencePointsMode); proj.SetAndroidSupportedAbis(supportedAbis); using (var b = CreateApkBuilder()) { Assert.IsTrue(b.Build(proj), "Build should have succeeded."); string intermediateOutputDir = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath); List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); Dictionary <string, string> envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.Count > 0, $"No environment variables defined"); string monoDebugVar; bool monoDebugVarFound = envvars.TryGetValue("MONO_DEBUG", out monoDebugVar); if (String.IsNullOrEmpty(sequencePointsMode)) { Assert.IsFalse(monoDebugVarFound, $"environment should not contain MONO_DEBUG={monoDebugVar}"); } else { Assert.IsTrue(monoDebugVarFound, "environment should contain MONO_DEBUG"); Assert.AreEqual("gen-compact-seq-points", monoDebugVar, "environment should contain MONO_DEBUG=gen-compact-seq-points"); } EnvironmentHelper.AssertValidEnvironmentSharedLibrary(intermediateOutputDir, AndroidSdkPath, AndroidNdkPath, supportedAbis); } }
public void CheckConcurrentGC() { var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; var gcVarName = "MONO_GC_PARAMS"; var expectedDefaultValue = "major=marksweep"; var expectedUpdatedValue = "major=marksweep-conc"; var supportedAbis = "armeabi-v7a;arm64-v8a"; proj.SetAndroidSupportedAbis(supportedAbis); using (var b = CreateApkBuilder()) { proj.SetProperty("AndroidEnableSGenConcurrent", "False"); Assert.IsTrue(b.Build(proj), "Build should have succeeded."); var intermediateOutputDir = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath); // AndroidEnableSGenConcurrent=False by default List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); Dictionary <string, string> envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.ContainsKey(gcVarName), $"Environment should contain '{gcVarName}'."); Assert.AreEqual(expectedDefaultValue, envvars[gcVarName], $"'{gcVarName}' should have been '{expectedDefaultValue}' when concurrent GC is disabled."); proj.SetProperty("AndroidEnableSGenConcurrent", "True"); Assert.IsTrue(b.Build(proj), "Second build should have succeeded."); envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.ContainsKey(gcVarName), $"Environment should contain '{gcVarName}'."); Assert.AreEqual(expectedUpdatedValue, envvars[gcVarName], $"'{gcVarName}' should have been '{expectedUpdatedValue}' when concurrent GC is enabled."); } }
public void EnvironmentFileContents() { string intermediateOutputDir = Path.Combine(testProjectPath, "obj", XABuildPaths.Configuration); List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, Xamarin.Android.Tools.XABuildConfig.SupportedABIs, true); EnvironmentHelper.ApplicationConfig app_config = EnvironmentHelper.ReadApplicationConfig(envFiles); Assert.That(app_config, Is.Not.Null, "application_config must be present in the environment files"); }
public void CheckBuildIdIsUnique([Values("apk", "aab")] string packageFormat) { const string supportedAbis = "armeabi-v7a;x86"; Dictionary <string, string> buildIds = new Dictionary <string, string> (); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; proj.SetProperty(proj.ReleaseProperties, "MonoSymbolArchive", "True"); proj.SetProperty(proj.ReleaseProperties, "DebugSymbols", "true"); proj.SetProperty(proj.ReleaseProperties, "DebugType", "PdbOnly"); proj.SetProperty(proj.ReleaseProperties, KnownProperties.AndroidCreatePackagePerAbi, "true"); proj.SetProperty(proj.ReleaseProperties, "AndroidPackageFormat", packageFormat); proj.SetAndroidSupportedAbis(supportedAbis); using (var b = CreateApkBuilder()) { b.Verbosity = Microsoft.Build.Framework.LoggerVerbosity.Diagnostic; b.ThrowOnBuildFailure = false; Assert.IsTrue(b.Build(proj), "first build failed"); var outputPath = Path.Combine(Root, b.ProjectDirectory, proj.OutputPath); var archivePath = Path.Combine(outputPath, $"{proj.PackageName}.{packageFormat}.mSYM"); var allFilesInArchive = Directory.GetFiles(archivePath, "*", SearchOption.AllDirectories); string extension = "dll"; Assert.IsTrue(allFilesInArchive.Any(x => Path.GetFileName(x) == $"{proj.ProjectName}.{extension}"), $"{proj.ProjectName}.{extension} should exist in {archivePath}"); extension = "pdb"; Assert.IsTrue(allFilesInArchive.Any(x => Path.GetFileName(x) == $"{proj.ProjectName}.{extension}"), $"{proj.ProjectName}.{extension} should exist in {archivePath}"); string intermediateOutputDir = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath); List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); Dictionary <string, string> envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.Count > 0, $"No environment variables defined"); string buildID; Assert.IsTrue(envvars.TryGetValue("XAMARIN_BUILD_ID", out buildID), "The environment should contain a XAMARIN_BUILD_ID"); buildIds.Add("all", buildID); var msymDirectory = Path.Combine(Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}.{packageFormat}.mSYM"); Assert.IsTrue(File.Exists(Path.Combine(msymDirectory, "manifest.xml")), "manifest.xml should exist in", msymDirectory); var doc = XDocument.Load(Path.Combine(msymDirectory, "manifest.xml")); Assert.IsTrue(doc.Element("mono-debug") .Elements() .Any(x => x.Name == "app-id" && x.Value == proj.PackageName), "app-id is has an incorrect value."); var buildId = buildIds.First().Value; Assert.IsTrue(doc.Element("mono-debug") .Elements() .Any(x => x.Name == "build-id" && x.Value == buildId), "build-id is has an incorrect value."); EnvironmentHelper.AssertValidEnvironmentSharedLibrary(intermediateOutputDir, AndroidSdkPath, AndroidNdkPath, supportedAbis); } }
public void BuildWithTlsProvider(string androidTlsProvider, bool isRelease, bool expected) { var proj = new XamarinAndroidApplicationProject() { IsRelease = isRelease, }; var supportedAbis = new string [] { "armeabi-v7a", "arm64-v8a" }; proj.SetAndroidSupportedAbis(supportedAbis); using (var b = CreateApkBuilder(Path.Combine("temp", $"BuildWithTlsProvider_{androidTlsProvider}_{isRelease}_{expected}"))) { proj.SetProperty("AndroidTlsProvider", androidTlsProvider); Assert.IsTrue(b.Build(proj), "Build should have succeeded."); var intermediateOutputDir = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath); var apk = Path.Combine(intermediateOutputDir, "android", "bin", "UnnamedProject.UnnamedProject.apk"); using (var zipFile = ZipHelper.OpenZip(apk)) { foreach (var abi in supportedAbis) { if (expected) { Assert.IsNotNull(ZipHelper.ReadFileFromZip(zipFile, $"lib/{abi}/libmono-btls-shared.so"), $"lib/{abi}/libmono-btls-shared.so should exist in the apk."); } else { Assert.IsNull(ZipHelper.ReadFileFromZip(zipFile, $"lib/{abi}/libmono-btls-shared.so"), $"lib/{abi}/libmono-btls-shared.so should not exist in the apk."); } } } List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, string.Join(";", supportedAbis), true); Dictionary <string, string> envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.ContainsKey("XA_TLS_PROVIDER"), "Environment should contain XA_TLS_PROVIDER."); if (androidTlsProvider == string.Empty) { Assert.AreEqual("btls", envvars["XA_TLS_PROVIDER"], "'XA_TLS_PROVIDER' should have been 'btls' when provider is not set."); } else { Assert.AreEqual(androidTlsProvider, envvars["XA_TLS_PROVIDER"], $"'XA_TLS_PROVIDER' should have been '{androidTlsProvider}'."); } } }
public void DotNetBuildLibrary(bool isRelease, bool duplicateAar) { var path = Path.Combine("temp", TestName); var env_var = "MY_ENVIRONMENT_VAR"; var env_val = "MY_VALUE"; // Setup dependencies App A -> Lib B -> Lib C var libC = new XASdkProject(outputType: "Library") { ProjectName = "LibraryC", IsRelease = isRelease, Sources = { new BuildItem.Source("Bar.cs") { TextContent = () => "public class Bar { }", } } }; libC.OtherBuildItems.Add(new AndroidItem.AndroidAsset("Assets\\bar\\bar.txt") { BinaryContent = () => Array.Empty <byte> (), }); var activity = libC.Sources.FirstOrDefault(s => s.Include() == "MainActivity.cs"); if (activity != null) { libC.Sources.Remove(activity); } var libCBuilder = CreateDotNetBuilder(libC, Path.Combine(path, libC.ProjectName)); Assert.IsTrue(libCBuilder.Build(), $"{libC.ProjectName} should succeed"); var libB = new XASdkProject(outputType: "Library") { ProjectName = "LibraryB", IsRelease = isRelease, Sources = { new BuildItem.Source("Foo.cs") { TextContent = () => "public class Foo : Bar { }", } } }; libB.OtherBuildItems.Add(new AndroidItem.AndroidAsset("Assets\\foo\\foo.txt") { BinaryContent = () => Array.Empty <byte> (), }); libB.OtherBuildItems.Add(new AndroidItem.AndroidResource("Resources\\layout\\MyLayout.axml") { TextContent = () => "<?xml version=\"1.0\" encoding=\"utf-8\" ?><LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" />" }); libB.OtherBuildItems.Add(new AndroidItem.AndroidResource("Resources\\raw\\bar.txt") { BinaryContent = () => Array.Empty <byte> (), }); libB.OtherBuildItems.Add(new AndroidItem.AndroidEnvironment("env.txt") { TextContent = () => $"{env_var}={env_val}", }); libB.OtherBuildItems.Add(new AndroidItem.AndroidEnvironment("sub\\directory\\env.txt") { TextContent = () => $"{env_var}={env_val}", }); libB.OtherBuildItems.Add(new AndroidItem.AndroidLibrary("sub\\directory\\foo.jar") { BinaryContent = () => Convert.FromBase64String(InlineData.JavaClassesJarBase64), }); libB.OtherBuildItems.Add(new AndroidItem.AndroidLibrary("sub\\directory\\arm64-v8a\\libfoo.so") { BinaryContent = () => Array.Empty <byte> (), }); libB.OtherBuildItems.Add(new AndroidItem.AndroidLibrary("libfoo.so") { MetadataValues = "Link=x86\\libfoo.so", BinaryContent = () => Array.Empty <byte> (), }); libB.AddReference(libC); activity = libB.Sources.FirstOrDefault(s => s.Include() == "MainActivity.cs"); if (activity != null) { libB.Sources.Remove(activity); } var libBBuilder = CreateDotNetBuilder(libB, Path.Combine(path, libB.ProjectName)); Assert.IsTrue(libBBuilder.Build(), $"{libB.ProjectName} should succeed"); // Check .aar file for class library var aarPath = Path.Combine(FullProjectDirectory, libB.OutputPath, $"{libB.ProjectName}.aar"); FileAssert.Exists(aarPath); using (var aar = ZipHelper.OpenZip(aarPath)) { aar.AssertContainsEntry(aarPath, "assets/foo/foo.txt"); aar.AssertContainsEntry(aarPath, "res/layout/mylayout.xml"); aar.AssertContainsEntry(aarPath, "res/raw/bar.txt"); aar.AssertContainsEntry(aarPath, ".net/__res_name_case_map.txt"); aar.AssertContainsEntry(aarPath, ".net/env/190E30B3D205731E.env"); aar.AssertContainsEntry(aarPath, ".net/env/2CBDAB7FEEA94B19.env"); aar.AssertContainsEntry(aarPath, "libs/A1AFA985571E728E.jar"); aar.AssertContainsEntry(aarPath, "jni/arm64-v8a/libfoo.so"); aar.AssertContainsEntry(aarPath, "jni/x86/libfoo.so"); } // Check EmbeddedResource files do not exist var assemblyPath = Path.Combine(FullProjectDirectory, libB.OutputPath, $"{libB.ProjectName}.dll"); FileAssert.Exists(assemblyPath); using (var assembly = AssemblyDefinition.ReadAssembly(assemblyPath)) { Assert.AreEqual(0, assembly.MainModule.Resources.Count); } var appA = new XASdkProject { ProjectName = "AppA", IsRelease = isRelease, Sources = { new BuildItem.Source("Bar.cs") { TextContent = () => "public class Bar : Foo { }", } } }; appA.AddReference(libB); if (duplicateAar) { // Test a duplicate @(AndroidLibrary) item with the same path of LibraryB.aar appA.OtherBuildItems.Add(new AndroidItem.AndroidLibrary(aarPath)); } var appBuilder = CreateDotNetBuilder(appA, Path.Combine(path, appA.ProjectName)); Assert.IsTrue(appBuilder.Build(), $"{appA.ProjectName} should succeed"); // Check .apk for assets, res, and native libraries var apkPath = Path.Combine(FullProjectDirectory, appA.OutputPath, $"{appA.PackageName}.apk"); FileAssert.Exists(apkPath); using (var apk = ZipHelper.OpenZip(apkPath)) { apk.AssertContainsEntry(apkPath, "assets/foo/foo.txt"); apk.AssertContainsEntry(apkPath, "assets/bar/bar.txt"); apk.AssertContainsEntry(aarPath, "res/layout/mylayout.xml"); apk.AssertContainsEntry(apkPath, "res/raw/bar.txt"); apk.AssertContainsEntry(apkPath, "lib/arm64-v8a/libfoo.so"); apk.AssertContainsEntry(apkPath, "lib/x86/libfoo.so"); } // Check classes.dex contains foo.jar var intermediate = Path.Combine(FullProjectDirectory, appA.IntermediateOutputPath); var dexFile = Path.Combine(intermediate, "android", "bin", "classes.dex"); FileAssert.Exists(dexFile); string className = "Lcom/xamarin/android/test/msbuildtest/JavaSourceJarTest;"; Assert.IsTrue(DexUtils.ContainsClass(className, dexFile, AndroidSdkPath), $"`{dexFile}` should include `{className}`!"); // Check environment variable var environmentFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediate, "x86", required: true); var environmentVariables = EnvironmentHelper.ReadEnvironmentVariables(environmentFiles); Assert.IsTrue(environmentVariables.TryGetValue(env_var, out string actual), $"Environment should contain {env_var}"); Assert.AreEqual(env_val, actual, $"{env_var} should be {env_val}"); // Check Resource.designer.cs var resource_designer_cs = Path.Combine(intermediate, "Resource.designer.cs"); FileAssert.Exists(resource_designer_cs); var resource_designer_text = File.ReadAllText(resource_designer_cs); StringAssert.Contains("public const int MyLayout", resource_designer_text); }
public void BuildApplicationWithMonoEnvironment([Values("", "Normal", "Offline")] string sequencePointsMode) { const string supportedAbis = "armeabi-v7a;x86"; var lib = new XamarinAndroidLibraryProject { ProjectName = "Library1", IsRelease = true, OtherBuildItems = { new AndroidItem.AndroidEnvironment("Mono.env") { TextContent = () => "MONO_DEBUG=soft-breakpoints" }, }, }; var app = new XamarinFormsAndroidApplicationProject() { IsRelease = true, AndroidLinkModeRelease = AndroidLinkMode.Full, References = { new BuildItem("ProjectReference", "..\\Library1\\Library1.csproj"), }, }; //LinkSkip one assembly that contains __AndroidLibraryProjects__.zip string linkSkip = "FormsViewGroup"; app.SetProperty("AndroidLinkSkip", linkSkip); app.SetProperty("_AndroidSequencePointsMode", sequencePointsMode); app.SetAndroidSupportedAbis(supportedAbis); using (var libb = CreateDllBuilder(Path.Combine("temp", TestName, lib.ProjectName))) using (var appb = CreateApkBuilder(Path.Combine("temp", TestName, app.ProjectName))) { Assert.IsTrue(libb.Build(lib), "Library build should have succeeded."); Assert.IsTrue(appb.Build(app), "App should have succeeded."); Assert.IsTrue(StringAssertEx.ContainsText(appb.LastBuildOutput, $"Save assembly: {linkSkip}"), $"{linkSkip} should be saved, and not linked!"); string intermediateOutputDir = Path.Combine(Root, appb.ProjectDirectory, app.IntermediateOutputPath); List <string> envFiles = EnvironmentHelper.GatherEnvironmentFiles(intermediateOutputDir, supportedAbis, true); Dictionary <string, string> envvars = EnvironmentHelper.ReadEnvironmentVariables(envFiles); Assert.IsTrue(envvars.Count > 0, $"No environment variables defined"); string monoDebugVar; Assert.IsTrue(envvars.TryGetValue("MONO_DEBUG", out monoDebugVar), "Environment should contain MONO_DEBUG"); Assert.IsFalse(String.IsNullOrEmpty(monoDebugVar), "Environment must contain MONO_DEBUG with a value"); Assert.IsTrue(monoDebugVar.IndexOf("soft-breakpoints") >= 0, "Environment must contain MONO_DEBUG with 'soft-breakpoints' in its value"); if (!String.IsNullOrEmpty(sequencePointsMode)) { Assert.IsTrue(monoDebugVar.IndexOf("gen-compact-seq-points") >= 0, "The values from Mono.env should have been merged into environment"); } EnvironmentHelper.AssertValidEnvironmentSharedLibrary(intermediateOutputDir, AndroidSdkPath, AndroidNdkPath, supportedAbis); var assemblyDir = Path.Combine(Root, appb.ProjectDirectory, app.IntermediateOutputPath, "android", "assets"); var rp = new ReaderParameters { ReadSymbols = false }; foreach (var assemblyFile in Directory.EnumerateFiles(assemblyDir, "*.dll")) { using (var assembly = AssemblyDefinition.ReadAssembly(assemblyFile)) { foreach (var module in assembly.Modules) { var resources = module.Resources.Select(r => r.Name).ToArray(); Assert.IsFalse(StringAssertEx.ContainsText(resources, "__AndroidEnvironment__"), "AndroidEnvironment EmbeddedResource should be stripped!"); Assert.IsFalse(StringAssertEx.ContainsText(resources, "__AndroidLibraryProjects__.zip"), "__AndroidLibraryProjects__.zip should be stripped!"); Assert.IsFalse(StringAssertEx.ContainsText(resources, "__AndroidNativeLibraries__.zip"), "__AndroidNativeLibraries__.zip should be stripped!"); } } } } }