public void LocalizedAssemblies_ShouldBeFastDeployed() { AssertCommercialBuild(); AssertHasDevices(); var path = Path.Combine("temp", TestName); var lib = new XamarinAndroidLibraryProject { ProjectName = "Localization", OtherBuildItems = { new BuildItem("EmbeddedResource", "Bar.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Bar.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") } } }; var app = new XamarinAndroidApplicationProject { EmbedAssembliesIntoApk = false, OtherBuildItems = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") } } }; app.References.Add(new BuildItem.ProjectReference($"..\\{lib.ProjectName}\\{lib.ProjectName}.csproj", lib.ProjectName, lib.ProjectGuid)); using (var libBuilder = CreateDllBuilder(Path.Combine(path, lib.ProjectName))) using (var appBuilder = CreateApkBuilder(Path.Combine(path, app.ProjectName))) { Assert.IsTrue(libBuilder.Build(lib), "Library Build should have succeeded."); Assert.IsTrue(appBuilder.Install(app), "App Install should have succeeded."); var projectOutputPath = Path.Combine(Root, appBuilder.ProjectDirectory, app.OutputPath); var resourceFilesFromDisk = Directory.EnumerateFiles(projectOutputPath, "*.resources.dll", SearchOption.AllDirectories) .Select(r => r = r.Replace(projectOutputPath, string.Empty).Replace("\\", "/")); var overrideContents = string.Empty; foreach (var dir in GetOverrideDirectoryPaths(app.PackageName)) { overrideContents += RunAdbCommand($"shell run-as {app.PackageName} find {dir}"); } Assert.IsTrue(resourceFilesFromDisk.Any(), $"Unable to find any localized assemblies in {resourceFilesFromDisk}"); foreach (var res in resourceFilesFromDisk) { StringAssert.Contains(res, overrideContents, $"{res} did not exist in the .__override__ directory.\nFound:{overrideContents}"); } appBuilder.BuildLogFile = "uninstall.log"; appBuilder.Uninstall(app); } }
public void MissingSatelliteAssemblyInApp() { var proj = new XamarinAndroidApplicationProject { IsRelease = true, OtherBuildItems = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") } } }; using (var b = CreateApkBuilder()) { b.Target = "Build"; Assert.IsTrue(b.Build(proj), "Build should have succeeded."); b.Target = "SignAndroidPackage"; Assert.IsTrue(b.Build(proj), "SignAndroidPackage should have succeeded."); var apk = Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", $"{proj.PackageName}.apk"); using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.ContainsEntry($"assemblies/es/{proj.ProjectName}.resources.dll"), "Apk should contain satellite assemblies!"); } } }
public void OneTimeSetUp() { var path = Path.Combine("temp", TestName); lib = new XamarinAndroidLibraryProject { ProjectName = "Localization", IsRelease = true, OtherBuildItems = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") } } }; var bytes = new byte [1024]; app = new XamarinFormsMapsApplicationProject { IsRelease = true, }; app.OtherBuildItems.Add(new AndroidItem.AndroidAsset("foo.bar") { BinaryContent = () => bytes, }); app.OtherBuildItems.Add(new AndroidItem.AndroidAsset("foo.wav") { BinaryContent = () => bytes, }); app.OtherBuildItems.Add(new BuildItem("None", "buildConfig.json") { TextContent = () => BuildConfig, }); app.SetProperty("AndroidStoreUncompressedFileExtensions", ".bar"); app.References.Add(new BuildItem.ProjectReference($"..\\{lib.ProjectName}\\{lib.ProjectName}.csproj", lib.ProjectName, lib.ProjectGuid)); //NOTE: this is here to enable adb shell run-as app.AndroidManifest = app.AndroidManifest.Replace("<application ", "<application android:debuggable=\"true\" "); app.SetProperty(app.ReleaseProperties, "AndroidPackageFormat", "aab"); var abis = new string [] { "armeabi-v7a", "arm64-v8a", "x86" }; app.SetProperty(KnownProperties.AndroidSupportedAbis, string.Join(";", abis)); app.SetProperty("AndroidBundleConfigurationFile", "buildConfig.json"); libBuilder = CreateDllBuilder(Path.Combine(path, lib.ProjectName), cleanupOnDispose: true); Assert.IsTrue(libBuilder.Build(lib), "Library build should have succeeded."); appBuilder = CreateApkBuilder(Path.Combine(path, app.ProjectName), cleanupOnDispose: true); Assert.IsTrue(appBuilder.Build(app), "App build should have succeeded."); var projectDir = Path.Combine(Root, appBuilder.ProjectDirectory); intermediate = Path.Combine(projectDir, app.IntermediateOutputPath); bin = Path.Combine(projectDir, app.OutputPath); }
public static void AddCultureResourcesToProject(IShortFormProject proj, string filename, string dataName, CultureTypes types = CultureTypes.AllCultures) { foreach (var culture in CultureInfo.GetCultures(types)) { proj.OtherBuildItems.Add(new BuildItem("EmbeddedResource", $"{filename}.{culture.Name}.resx") { TextContent = () => InlineData.ResxWithContents($"<data name=\"{dataName}\"><value>{culture.Name}</value></data>") }); } }
public void LocalizedAssemblies_ShouldBeFastDeployed() { AssertCommercialBuild(); AssertHasDevices(); var proj = new XamarinAndroidApplicationProject { AndroidUseSharedRuntime = true, EmbedAssembliesIntoApk = false, OtherBuildItems = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") } } }; using (var builder = CreateApkBuilder()) { Assert.IsTrue(builder.Install(proj), "Install should have succeeded."); var projectOutputPath = Path.Combine(Root, builder.ProjectDirectory, proj.OutputPath); var resourceFilesFromDisk = Directory.EnumerateFiles(projectOutputPath, "*.resources.dll", SearchOption.AllDirectories) .Select(r => r = r.Replace(projectOutputPath, string.Empty).Replace("\\", "/")); var overrideContents = string.Empty; foreach (var dir in GetOverrideDirectoryPaths(proj.PackageName)) { overrideContents += RunAdbCommand($"shell find {dir}"); } builder.Uninstall(proj); Assert.IsTrue(resourceFilesFromDisk.Any(), $"Unable to find any localized assemblies in {resourceFilesFromDisk}"); foreach (var res in resourceFilesFromDisk) { StringAssert.Contains(res, overrideContents, $"{res} did not exist in the .__override__ directory.\nFound:{overrideContents}"); } } }
public void MissingSatelliteAssemblyInLibrary() { var path = Path.Combine("temp", TestName); var lib = new XamarinAndroidLibraryProject { ProjectName = "Localization", OtherBuildItems = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") } } }; var app = new XamarinAndroidApplicationProject { IsRelease = true, }; app.References.Add(new BuildItem.ProjectReference($"..\\{lib.ProjectName}\\{lib.ProjectName}.csproj", lib.ProjectName, lib.ProjectGuid)); using (var libBuilder = CreateDllBuilder(Path.Combine(path, lib.ProjectName))) using (var appBuilder = CreateApkBuilder(Path.Combine(path, app.ProjectName))) { Assert.IsTrue(libBuilder.Build(lib), "Library Build should have succeeded."); appBuilder.Target = "Build"; Assert.IsTrue(appBuilder.Build(app), "App Build should have succeeded."); appBuilder.Target = "SignAndroidPackage"; Assert.IsTrue(appBuilder.Build(app), "App SignAndroidPackage should have succeeded."); var apk = Path.Combine(Root, appBuilder.ProjectDirectory, app.IntermediateOutputPath, "android", "bin", $"{app.PackageName}.apk"); using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.ContainsEntry($"assemblies/es/{lib.ProjectName}.resources.dll"), "Apk should contain satellite assemblies!"); } } }
public void DotNetBuild(string runtimeIdentifiers, bool isRelease) { var proj = new XASdkProject { IsRelease = isRelease, Sources = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") }, } }; proj.OtherBuildItems.Add(new AndroidItem.InputJar("javaclasses.jar") { BinaryContent = () => ResourceData.JavaSourceJarTestJar, }); proj.OtherBuildItems.Add(new BuildItem("JavaSourceJar", "javaclasses-sources.jar") { BinaryContent = () => ResourceData.JavaSourceJarTestSourcesJar, }); if (!runtimeIdentifiers.Contains(";")) { proj.SetProperty(KnownProperties.RuntimeIdentifier, runtimeIdentifiers); } else { proj.SetProperty(KnownProperties.RuntimeIdentifiers, runtimeIdentifiers); } var dotnet = CreateDotNetBuilder(proj); Assert.IsTrue(dotnet.Build(), "`dotnet build` should succeed"); // TODO: run for release once illink warnings are gone // context: https://github.com/xamarin/xamarin-android/issues/4708 if (!isRelease) { Assert.IsTrue(StringAssertEx.ContainsText(dotnet.LastBuildOutput, " 0 Warning(s)"), "Should have no MSBuild warnings."); } var outputPath = Path.Combine(FullProjectDirectory, proj.OutputPath); if (!runtimeIdentifiers.Contains(";")) { outputPath = Path.Combine(outputPath, runtimeIdentifiers); } var files = Directory.EnumerateFileSystemEntries(outputPath) .Select(Path.GetFileName) .OrderBy(f => f) .ToArray(); var expectedFiles = new[] { "es", $"{proj.ProjectName}.dll", $"{proj.ProjectName}.pdb", $"{proj.PackageName}.apk", $"{proj.PackageName}-Signed.apk", $"{proj.ProjectName}.xml", }; CollectionAssert.AreEqual(expectedFiles, files, $"Expected: {string.Join (";", expectedFiles)}\n Found: {string.Join (";", files)}"); var assemblyPath = Path.Combine(outputPath, $"{proj.ProjectName}.dll"); FileAssert.Exists(assemblyPath); using (var assembly = AssemblyDefinition.ReadAssembly(assemblyPath)) { var typeName = "Com.Xamarin.Android.Test.Msbuildtest.JavaSourceJarTest"; var type = assembly.MainModule.GetType(typeName); Assert.IsNotNull(type, $"{assemblyPath} should contain {typeName}"); } bool expectEmbeddedAssembies = !(CommercialBuildAvailable && !isRelease); var apkPath = Path.Combine(outputPath, "UnnamedProject.UnnamedProject.apk"); FileAssert.Exists(apkPath); using (var apk = ZipHelper.OpenZip(apkPath)) { apk.AssertContainsEntry(apkPath, $"assemblies/{proj.ProjectName}.dll", shouldContainEntry: expectEmbeddedAssembies); apk.AssertContainsEntry(apkPath, $"assemblies/{proj.ProjectName}.pdb", shouldContainEntry: !CommercialBuildAvailable && !isRelease); apk.AssertContainsEntry(apkPath, $"assemblies/System.Linq.dll", shouldContainEntry: expectEmbeddedAssembies); apk.AssertContainsEntry(apkPath, $"assemblies/es/{proj.ProjectName}.resources.dll", shouldContainEntry: expectEmbeddedAssembies); var rids = runtimeIdentifiers.Split(';'); foreach (var abi in rids.Select(MonoAndroidHelper.RuntimeIdentifierToAbi)) { apk.AssertContainsEntry(apkPath, $"lib/{abi}/libmonodroid.so"); apk.AssertContainsEntry(apkPath, $"lib/{abi}/libmonosgen-2.0.so"); if (rids.Length > 1) { apk.AssertContainsEntry(apkPath, $"assemblies/{abi}/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); apk.AssertContainsEntry(apkPath, $"assemblies/{abi}/System.Collections.Concurrent.dll", shouldContainEntry: expectEmbeddedAssembies); } else { apk.AssertContainsEntry(apkPath, "assemblies/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); apk.AssertContainsEntry(apkPath, "assemblies/System.Collections.Concurrent.dll", shouldContainEntry: expectEmbeddedAssembies); } } } }
public void DotNetBuild(string runtimeIdentifiers, bool isRelease) { var proj = new XASdkProject { IsRelease = isRelease, ExtraNuGetConfigSources = { "https://pkgs.dev.azure.com/azure-public/vside/_packaging/xamarin-impl/nuget/v3/index.json" }, PackageReferences = { new Package { Id = "Xamarin.AndroidX.AppCompat", Version = "1.2.0.7-net6preview01" }, new Package { Id = "Microsoft.AspNetCore.Components.WebView", Version = "6.0.0-preview.5.21301.17" }, new Package { Id = "Microsoft.Extensions.FileProviders.Embedded", Version = "6.0.0-preview.6.21306.3" }, new Package { Id = "Microsoft.JSInterop", Version = "6.0.0-preview.6.21306.3" }, new Package { Id = "System.Text.Json", Version = "6.0.0-preview.7.21323.3" }, }, Sources = { new BuildItem("EmbeddedResource", "Foo.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancel</value></data>") }, new BuildItem("EmbeddedResource", "Foo.es.resx") { TextContent = () => InlineData.ResxWithContents("<data name=\"CancelButton\"><value>Cancelar</value></data>") }, } }; proj.MainActivity = proj.DefaultMainActivity.Replace(": Activity", ": AndroidX.AppCompat.App.AppCompatActivity"); proj.OtherBuildItems.Add(new AndroidItem.InputJar("javaclasses.jar") { BinaryContent = () => ResourceData.JavaSourceJarTestJar, }); proj.OtherBuildItems.Add(new BuildItem("JavaSourceJar", "javaclasses-sources.jar") { BinaryContent = () => ResourceData.JavaSourceJarTestSourcesJar, }); if (!runtimeIdentifiers.Contains(";")) { proj.SetProperty(KnownProperties.RuntimeIdentifier, runtimeIdentifiers); } else { proj.SetProperty(KnownProperties.RuntimeIdentifiers, runtimeIdentifiers); } var dotnet = CreateDotNetBuilder(proj); Assert.IsTrue(dotnet.Build(), "`dotnet build` should succeed"); dotnet.AssertHasNoWarnings(); var outputPath = Path.Combine(FullProjectDirectory, proj.OutputPath); var intermediateOutputPath = Path.Combine(FullProjectDirectory, proj.IntermediateOutputPath); if (!runtimeIdentifiers.Contains(";")) { outputPath = Path.Combine(outputPath, runtimeIdentifiers); intermediateOutputPath = Path.Combine(intermediateOutputPath, runtimeIdentifiers); } var files = Directory.EnumerateFileSystemEntries(outputPath) .Select(Path.GetFileName) .OrderBy(f => f) .ToArray(); var expectedFiles = new[] { $"{proj.PackageName}.apk", $"{proj.PackageName}-Signed.apk", "es", $"{proj.ProjectName}.dll", $"{proj.ProjectName}.pdb", $"{proj.ProjectName}.runtimeconfig.json", $"{proj.ProjectName}.xml", }; CollectionAssert.AreEqual(expectedFiles, files, $"Expected: {string.Join (";", expectedFiles)}\n Found: {string.Join (";", files)}"); var assemblyPath = Path.Combine(outputPath, $"{proj.ProjectName}.dll"); FileAssert.Exists(assemblyPath); using (var assembly = AssemblyDefinition.ReadAssembly(assemblyPath)) { var typeName = "Com.Xamarin.Android.Test.Msbuildtest.JavaSourceJarTest"; var type = assembly.MainModule.GetType(typeName); Assert.IsNotNull(type, $"{assemblyPath} should contain {typeName}"); } var rids = runtimeIdentifiers.Split(';'); if (isRelease) { // Check for stripped native libraries foreach (var rid in rids) { FileAssert.Exists(Path.Combine(intermediateOutputPath, "native", rid, "libmono-android.release.so")); FileAssert.Exists(Path.Combine(intermediateOutputPath, "native", rid, "libmonosgen-2.0.so")); } } bool expectEmbeddedAssembies = !(CommercialBuildAvailable && !isRelease); var apkPath = Path.Combine(outputPath, $"{proj.PackageName}.apk"); FileAssert.Exists(apkPath); using (var apk = ZipHelper.OpenZip(apkPath)) { apk.AssertContainsEntry(apkPath, $"assemblies/{proj.ProjectName}.dll", shouldContainEntry: expectEmbeddedAssembies); apk.AssertContainsEntry(apkPath, $"assemblies/{proj.ProjectName}.pdb", shouldContainEntry: !CommercialBuildAvailable && !isRelease); apk.AssertContainsEntry(apkPath, $"assemblies/System.Linq.dll", shouldContainEntry: expectEmbeddedAssembies); apk.AssertContainsEntry(apkPath, $"assemblies/es/{proj.ProjectName}.resources.dll", shouldContainEntry: expectEmbeddedAssembies); foreach (var abi in rids.Select(AndroidRidAbiHelper.RuntimeIdentifierToAbi)) { apk.AssertContainsEntry(apkPath, $"lib/{abi}/libmonodroid.so"); apk.AssertContainsEntry(apkPath, $"lib/{abi}/libmonosgen-2.0.so"); if (rids.Length > 1) { apk.AssertContainsEntry(apkPath, $"assemblies/{abi}/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); } else { apk.AssertContainsEntry(apkPath, "assemblies/System.Private.CoreLib.dll", shouldContainEntry: expectEmbeddedAssembies); } } } }