public void TargetFrameworkPairing(string description, ApiInfo[] androidSdk, ApiInfo[] targetFrameworks, string userSelected, string androidApiLevel, string androidApiLevelName, string targetFrameworkVersion) { var path = Path.Combine("temp", $"{nameof (TargetFrameworkPairing)}_{description}"); var androidSdkPath = CreateFauxAndroidSdkDirectory(Path.Combine(path, "android-sdk"), "26.0.3", androidSdk); var androidNdkPath = CreateFauxAndroidNdkDirectory(Path.Combine(path, "android-ndk")); string javaExe = string.Empty; string javacExe; var javaPath = CreateFauxJavaSdkDirectory(Path.Combine(path, "jdk"), "1.8.0", out javaExe, out javacExe); var referencePath = CreateFauxReferencesDirectory(Path.Combine(path, "references"), targetFrameworks); IBuildEngine engine = new MockBuildEngine(TestContext.Out); var resolveSdks = new ResolveSdks { BuildEngine = engine, AndroidSdkPath = androidSdkPath, AndroidNdkPath = androidNdkPath, JavaSdkPath = javaPath, MinimumSupportedJavaVersion = MinimumSupportedJavaVersion, LatestSupportedJavaVersion = LatestSupportedJavaVersion, ReferenceAssemblyPaths = new [] { Path.Combine(referencePath, "MonoAndroid"), }, }; var androidTooling = new ResolveAndroidTooling { BuildEngine = engine, AndroidSdkPath = androidSdkPath, UseLatestAndroidPlatformSdk = true, TargetFrameworkVersion = userSelected, AndroidApplication = true, }; Assert.IsTrue(resolveSdks.Execute(), "ResolveSdks should succeed!"); Assert.IsTrue(androidTooling.Execute(), "ResolveAndroidTooling should succeed!"); Assert.AreEqual(androidApiLevel, androidTooling.AndroidApiLevel, $"AndroidApiLevel should be {androidApiLevel}"); Assert.AreEqual(androidApiLevelName, androidTooling.AndroidApiLevelName, $"AndroidApiLevelName should be {androidApiLevelName}"); Assert.AreEqual(targetFrameworkVersion, androidTooling.TargetFrameworkVersion, $"TargetFrameworkVersion should be {targetFrameworkVersion}"); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void CheckClassIsReplacedWithMd5() { var path = Path.Combine(Root, "temp", "CheckClassIsReplacedWithMd5"); Directory.CreateDirectory(path); var resPath = Path.Combine(path, "res"); Directory.CreateDirectory(Path.Combine(resPath, "layout")); File.WriteAllText(Path.Combine(resPath, "layout", "main.xml"), @"<?xml version='1.0' ?> <LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'> <ClassLibrary1.CustomView xmlns:android='http://schemas.android.com/apk/res/android' /> <classlibrary1.CustomView xmlns:android='http://schemas.android.com/apk/res/android' /> </LinearLayout> "); var errors = new List <BuildErrorEventArgs> (); IBuildEngine engine = new MockBuildEngine(TestContext.Out, errors); var task = new ConvertResourcesCases { BuildEngine = engine }; task.ResourceDirectories = new ITaskItem [] { new TaskItem(resPath), }; task.AcwMapFile = Path.Combine(path, "acwmap.txt"); File.WriteAllLines(task.AcwMapFile, new string [] { "ClassLibrary1.CustomView;md5d6f7135293df7527c983d45d07471c5e.CustomTextView", "classlibrary1.CustomView;md5d6f7135293df7527c983d45d07471c5e.CustomTextView", }); Assert.IsTrue(task.Execute(), "Task should have executed successfully"); var output = File.ReadAllText(Path.Combine(resPath, "layout", "main.xml")); StringAssert.Contains("md5d6f7135293df7527c983d45d07471c5e.CustomTextView", output, "md5d6f7135293df7527c983d45d07471c5e.CustomTextView should exist in the main.xml"); StringAssert.DoesNotContain("ClassLibrary1.CustomView", output, "ClassLibrary1.CustomView should have been replaced."); StringAssert.DoesNotContain("classlibrary1.CustomView", output, "classlibrary1.CustomView should have been replaced."); Directory.Delete(path, recursive: true); }
public void GenerateDesignerFile() { var path = Path.Combine("temp", TestName); Directory.CreateDirectory(Path.Combine(Root, path, "res", "values")); Directory.CreateDirectory(Path.Combine(Root, path, "res", "transition")); Directory.CreateDirectory(Path.Combine(Root, path, "res", "raw")); Directory.CreateDirectory(Path.Combine(Root, path, "res", "layout")); File.WriteAllText(Path.Combine(Root, path, "res", "values", "strings.xml"), StringsXml); File.WriteAllText(Path.Combine(Root, path, "res", "transition", "transition.xml"), Transition); File.WriteAllText(Path.Combine(Root, path, "res", "raw", "foo.txt"), "Foo"); File.WriteAllText(Path.Combine(Root, path, "res", "layout", "main.xml"), Main); Directory.CreateDirectory(Path.Combine(Root, path, "lp", "res", "animator")); Directory.CreateDirectory(Path.Combine(Root, path, "lp", "res", "font")); Directory.CreateDirectory(Path.Combine(Root, path, "lp", "res", "values")); Directory.CreateDirectory(Path.Combine(Root, path, "lp", "res", "drawable")); Directory.CreateDirectory(Path.Combine(Root, path, "lp", "res", "menu")); Directory.CreateDirectory(Path.Combine(Root, path, "lp", "res", "mipmap-hdpi")); File.WriteAllText(Path.Combine(Root, path, "lp", "res", "animator", "slide_in_bottom.xml"), Animator); File.WriteAllText(Path.Combine(Root, path, "lp", "res", "font", "arial.ttf"), ""); File.WriteAllText(Path.Combine(Root, path, "lp", "res", "values", "strings.xml"), StringsXml2); File.WriteAllText(Path.Combine(Root, path, "lp", "res", "values", "dimen.xml"), Dimen); using (var stream = typeof(XamarinAndroidCommonProject).Assembly.GetManifestResourceStream("Xamarin.ProjectTools.Resources.Base.Icon.png")) { var icon_binary_mdpi = new byte [stream.Length]; stream.Read(icon_binary_mdpi, 0, (int)stream.Length); File.WriteAllBytes(Path.Combine(Root, path, "lp", "res", "drawable", "ic_menu_preferences.png"), icon_binary_mdpi); File.WriteAllBytes(Path.Combine(Root, path, "lp", "res", "mipmap-hdpi", "icon.png"), icon_binary_mdpi); } File.WriteAllText(Path.Combine(Root, path, "lp", "res", "menu", "Options.xml"), Menu); IBuildEngine engine = new MockBuildEngine(TestContext.Out); var task = new GenerateResourceDesigner { BuildEngine = engine }; task.UseManagedResourceGenerator = true; task.DesignTimeBuild = true; task.Namespace = "Foo.Foo"; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.cs"); task.ProjectDir = Path.Combine(Root, path); task.ResourceDirectory = Path.Combine(Root, path, "res") + Path.DirectorySeparatorChar; task.Resources = new TaskItem [] { new TaskItem(Path.Combine(Root, path, "res", "values", "strings.xml"), new Dictionary <string, string> () { { "LogicalName", "values\\strings.xml" }, }), }; task.AdditionalResourceDirectories = new TaskItem [] { new TaskItem(Path.Combine(Root, path, "lp", "res")), }; task.IsApplication = true; Assert.IsTrue(task.Execute(), "Task should have executed successfully."); Assert.IsTrue(File.Exists(task.NetResgenOutputFile), $"{task.NetResgenOutputFile} should have been created."); var expected = Path.Combine(Root, "Expected", "GenerateDesignerFileExpected.cs"); Assert.IsTrue(FileCompare(task.NetResgenOutputFile, expected), $"{task.NetResgenOutputFile} and {expected} do not match."); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void GenerateDesignerFileWithElevenStyleableAttributesFromRtxt() { var styleable = @"<resources> <declare-styleable name = ""ElevenAttributes""> <attr name = ""attr00"" format=""string"" /> <attr name = ""attr01"" format=""string"" /> <attr name = ""attr02"" format=""string"" /> <attr name = ""attr03"" format=""string"" /> <attr name = ""attr04"" format=""string"" /> <attr name = ""attr05"" format=""string"" /> <attr name = ""attr06"" format=""string"" /> <attr name = ""attr07"" format=""string"" /> <attr name = ""attr08"" format=""string"" /> <attr name = ""attr09"" format=""string"" /> <attr name = ""attr10"" format=""string"" /> </declare-styleable> </resources>"; var rtxt = @"int attr attr00 0x7f010000 int attr attr01 0x7f010001 int attr attr02 0x7f010002 int attr attr03 0x7f010003 int attr attr04 0x7f010004 int attr attr05 0x7f010005 int attr attr06 0x7f010006 int attr attr07 0x7f010007 int attr attr08 0x7f010008 int attr attr09 0x7f010009 int attr attr10 0x7f01000a int[] styleable ElevenAttributes { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005, 0x7f010006, 0x7f010007, 0x7f010008, 0x7f010009, 0x7f01000a } int styleable ElevenAttributes_attr00 0 int styleable ElevenAttributes_attr01 1 int styleable ElevenAttributes_attr02 2 int styleable ElevenAttributes_attr03 3 int styleable ElevenAttributes_attr04 4 int styleable ElevenAttributes_attr05 5 int styleable ElevenAttributes_attr06 6 int styleable ElevenAttributes_attr07 7 int styleable ElevenAttributes_attr08 8 int styleable ElevenAttributes_attr09 9 int styleable ElevenAttributes_attr10 10"; var path = Path.Combine("temp", TestName); Directory.CreateDirectory(Path.Combine(Root, path)); File.WriteAllText(Path.Combine(Root, path, "AndroidManifest.xml"), AndroidManifest); Directory.CreateDirectory(Path.Combine(Root, path, "res")); Directory.CreateDirectory(Path.Combine(Root, path, "res", "values")); File.WriteAllText(Path.Combine(Root, path, "res", "values", "attrs.xml"), styleable); File.WriteAllText(Path.Combine(Root, path, "R.txt"), rtxt); IBuildEngine engine = new MockBuildEngine(TestContext.Out); var task = new GenerateResourceDesigner { BuildEngine = engine }; task.UseManagedResourceGenerator = true; task.DesignTimeBuild = true; task.Namespace = "Foo.Foo"; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.cs"); task.ProjectDir = Path.Combine(Root, path); task.ResourceDirectory = Path.Combine(Root, path, "res"); task.Resources = new TaskItem [] {}; task.IsApplication = true; task.JavaPlatformJarPath = Path.Combine(AndroidSdkDirectory, "platforms", "android-27", "android.jar"); Assert.IsTrue(task.Execute(), "Task should have executed successfully."); Assert.IsTrue(File.Exists(task.NetResgenOutputFile), $"{task.NetResgenOutputFile} should have been created."); var expected = Path.Combine(Root, "Expected", "GenerateDesignerFileWithElevenStyleableAttributesExpected.cs"); CompareFilesIgnoreRuntimeInfoString(task.NetResgenOutputFile, expected); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void CompareAaptAndManagedParserOutputWithCustomIds() { var path = Path.Combine("temp", TestName); CreateResourceDirectory(path); File.WriteAllText(Path.Combine(Root, path, "res", "layout", "custom.xml"), CustomId); File.WriteAllText(Path.Combine(Root, path, "foo.map"), @"a\nb"); Directory.CreateDirectory(Path.Combine(Root, path, "java")); string resPath = Path.Combine(Root, path, "res"); int platform = 0; using (var b = new Builder()) { platform = b.GetMaxInstalledPlatform(); } IBuildEngine engine = new MockBuildEngine(TestContext.Out); var aapt = new Aapt() { BuildEngine = engine, ToolPath = GetPathToAapt(), ResourceDirectory = resPath, ManifestFiles = new ITaskItem [] { new TaskItem(Path.Combine(Root, path, "AndroidManifest.xml")) }, ResourceOutputFile = Path.Combine(Root, path, "foo.apk"), AssemblyIdentityMapFile = Path.Combine(Root, path, "foo.map"), JavaPlatformJarPath = Path.Combine(AndroidSdkDirectory, "platforms", $"android-{platform}", "android.jar"), JavaDesignerOutputDirectory = Path.Combine(Root, path, "java"), ResourceSymbolsTextFileDirectory = Path.Combine(Root, path), AdditionalResourceDirectories = new ITaskItem [] { new TaskItem(Path.Combine(Root, path, "lp", "res")) }, AndroidUseLatestPlatformSdk = true, ApiLevel = $"{platform}", }; Assert.IsTrue(aapt.Execute(), "Aapt should have succeeded."); string rTxt = Path.Combine(Root, path, "R.txt"); FileAssert.Exists(rTxt, $"{rTxt} should have been created."); var task = new GenerateResourceDesigner { BuildEngine = engine }; task.UseManagedResourceGenerator = true; task.DesignTimeBuild = false; task.Namespace = "MonoAndroidApplication4.MonoAndroidApplication4"; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.aapt.cs"); task.ProjectDir = Path.Combine(Root, path); task.ResourceDirectory = Path.Combine(Root, path, "res") + Path.DirectorySeparatorChar; task.Resources = new TaskItem [] { new TaskItem(Path.Combine(Root, path, "res", "values", "strings.xml"), new Dictionary <string, string> () { { "LogicalName", "values\\strings.xml" }, }), }; task.AdditionalResourceDirectories = new TaskItem [] { new TaskItem(Path.Combine(Root, path, "lp", "res")), }; task.ResourceFlagFile = Path.Combine(Root, path, "AndroidResgen.flag"); task.IsApplication = true; task.JavaPlatformJarPath = aapt.JavaPlatformJarPath; Assert.IsTrue(task.Execute(), "Task should have executed successfully."); string aaptDesigner = Path.Combine(Root, path, "Resource.designer.aapt.cs"); var aaptDesignerText = File.ReadAllText(aaptDesigner); StringAssert.Contains("MyCustomID", aaptDesignerText, ""); StringAssert.Contains("HelloWorldTextView", aaptDesignerText, ""); StringAssert.Contains("ACustomID", aaptDesignerText, ""); StringAssert.Contains("foo1", aaptDesignerText, ""); task.UseManagedResourceGenerator = true; task.DesignTimeBuild = true; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.managedrtxt.cs"); Assert.IsTrue(task.Execute(), "Task should have executed successfully."); string managedDesignerRtxt = Path.Combine(Root, path, "Resource.designer.managedrtxt.cs"); CompareFilesIgnoreRuntimeInfoString(managedDesignerRtxt, aaptDesigner); File.WriteAllText(task.ResourceFlagFile, string.Empty); File.Delete(Path.Combine(Root, path, "R.txt.bak")); File.Move(rTxt, Path.Combine(Root, path, "R.txt.bak")); task.UseManagedResourceGenerator = true; task.DesignTimeBuild = true; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.managed.cs"); Assert.IsTrue(task.Execute(), "Task should have executed successfully."); string managedDesigner = Path.Combine(Root, path, "Resource.designer.managed.cs"); var managedDesignerText = File.ReadAllText(managedDesigner); StringAssert.Contains("MyCustomID", managedDesignerText, ""); StringAssert.Contains("HelloWorldTextView", managedDesignerText, ""); StringAssert.Contains("ACustomID", managedDesignerText, ""); StringAssert.Contains("foo1", managedDesignerText, ""); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void CompareAapt2AndManagedParserOutput() { var path = Path.Combine("temp", TestName); CreateResourceDirectory(path); File.WriteAllText(Path.Combine(Root, path, "foo.map"), @"a\nb"); Directory.CreateDirectory(Path.Combine(Root, path, "java")); IBuildEngine engine = new MockBuildEngine(TestContext.Out); var aapt2Compile = new Aapt2Compile { BuildEngine = engine, ToolPath = GetPathToAapt2(), ResourceDirectories = new ITaskItem [] { new TaskItem(Path.Combine(Root, path, "lp", "res"), new Dictionary <string, string> { { "Hash", "lp" } }), new TaskItem(Path.Combine(Root, path, "res"), new Dictionary <string, string> { { "Hash", "compiled" } }), }, FlatArchivesDirectory = Path.Combine(Root, path), }; Assert.IsTrue(aapt2Compile.Execute(), "Aapt2 Compile should have succeeded."); int platform = 0; using (var b = new Builder()) { platform = b.GetMaxInstalledPlatform(); } string resPath = Path.Combine(Root, path, "res"); string rTxt = Path.Combine(Root, path, "R.txt"); var aapt2Link = new Aapt2Link { BuildEngine = engine, ToolPath = GetPathToAapt2(), ResourceDirectories = new ITaskItem [] { new TaskItem(resPath) }, ManifestFiles = new ITaskItem [] { new TaskItem(Path.Combine(Root, path, "AndroidManifest.xml")) }, AdditionalResourceArchives = new ITaskItem [] { new TaskItem(Path.Combine(Root, path, "lp.flata")) }, CompiledResourceFlatArchive = new TaskItem(Path.Combine(Root, path, "compiled.flata")), OutputFile = Path.Combine(Root, path, "foo.apk"), AssemblyIdentityMapFile = Path.Combine(Root, path, "foo.map"), JavaPlatformJarPath = Path.Combine(AndroidSdkDirectory, "platforms", $"android-{platform}", "android.jar"), JavaDesignerOutputDirectory = Path.Combine(Root, path, "java"), ResourceSymbolsTextFile = rTxt, }; Assert.IsTrue(aapt2Link.Execute(), "Aapt2 Link should have succeeded."); FileAssert.Exists(rTxt, $"{rTxt} should have been created."); var task = new GenerateResourceDesigner { BuildEngine = engine }; task.UseManagedResourceGenerator = true; task.DesignTimeBuild = false; task.Namespace = "MonoAndroidApplication4.MonoAndroidApplication4"; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.aapt2.cs"); task.ProjectDir = Path.Combine(Root, path); task.ResourceDirectory = Path.Combine(Root, path, "res") + Path.DirectorySeparatorChar; task.Resources = new TaskItem [] { new TaskItem(Path.Combine(Root, path, "res", "values", "strings.xml"), new Dictionary <string, string> () { { "LogicalName", "values\\strings.xml" }, }), }; task.AdditionalResourceDirectories = new TaskItem [] { new TaskItem(Path.Combine(Root, path, "lp", "res")), }; task.ResourceFlagFile = Path.Combine(Root, path, "AndroidResgen.flag"); task.IsApplication = true; task.JavaPlatformJarPath = aapt2Link.JavaPlatformJarPath; Assert.IsTrue(task.Execute(), "Task should have executed successfully."); File.WriteAllText(task.ResourceFlagFile, string.Empty); File.Delete(Path.Combine(Root, path, "R.txt.bak")); File.Move(rTxt, Path.Combine(Root, path, "R.txt.bak")); task.UseManagedResourceGenerator = true; task.DesignTimeBuild = true; task.NetResgenOutputFile = Path.Combine(Root, path, "Resource.designer.managed.cs"); Assert.IsTrue(task.Execute(), "Task should have executed successfully."); string aapt2Designer = Path.Combine(Root, path, "Resource.designer.aapt2.cs"); string managedDesigner = Path.Combine(Root, path, "Resource.designer.managed.cs"); CompareFilesIgnoreRuntimeInfoString(managedDesigner, aapt2Designer); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void Setup() { engine = new MockBuildEngine(TestContext.Out, errors = new List <BuildErrorEventArgs> (), warnings = new List <BuildWarningEventArgs> (), messages = new List <BuildMessageEventArgs> ()); temp = Path.GetTempFileName(); keyToolPath = Path.Combine(AndroidSdkResolver.GetJavaSdkPath(), "bin"); }
public void ResolveSdkTiming() { var path = Path.Combine("temp", TestName); var androidSdkPath = CreateFauxAndroidSdkDirectory(Path.Combine(path, "android-sdk"), "26.0.3"); string javaExe = string.Empty; string javacExe; var javaPath = CreateFauxJavaSdkDirectory(Path.Combine(path, "jdk"), "1.8.0", out javaExe, out javacExe); var referencePath = CreateFauxReferencesDirectory(Path.Combine(path, "references"), new ApiInfo [] { new ApiInfo() { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true }, new ApiInfo() { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true }, }); IBuildEngine engine = new MockBuildEngine(TestContext.Out); var resolveSdks = new ResolveSdks { BuildEngine = engine, AndroidSdkPath = androidSdkPath, AndroidNdkPath = androidSdkPath, JavaSdkPath = javaPath, ReferenceAssemblyPaths = new [] { Path.Combine(referencePath, "MonoAndroid"), }, }; var validateJavaVersion = new ValidateJavaVersion { BuildEngine = engine, TargetFrameworkVersion = "v8.0", AndroidSdkBuildToolsVersion = "26.0.3", JavaSdkPath = javaPath, JavaToolExe = javaExe, JavacToolExe = javacExe, LatestSupportedJavaVersion = "1.8.0", MinimumSupportedJavaVersion = "1.7.0", }; var androidTooling = new ResolveAndroidTooling { BuildEngine = engine, AndroidSdkPath = androidSdkPath, AndroidNdkPath = androidSdkPath, TargetFrameworkVersion = "v8.0", AndroidSdkBuildToolsVersion = "26.0.3", UseLatestAndroidPlatformSdk = false, AotAssemblies = false, SequencePointsMode = "None", }; var start = DateTime.UtcNow; Assert.IsTrue(resolveSdks.Execute(), "ResolveSdks should succeed!"); Assert.IsTrue(validateJavaVersion.Execute(), "ValidateJavaVersion should succeed!"); Assert.IsTrue(androidTooling.Execute(), "ResolveAndroidTooling should succeed!"); var executionTime = DateTime.UtcNow - start; Assert.LessOrEqual(executionTime, TimeSpan.FromSeconds(2), "Task should not take more than 2 seconds to run."); Assert.AreEqual(androidTooling.AndroidApiLevel, "26", "AndroidApiLevel should be 26"); Assert.AreEqual(androidTooling.TargetFrameworkVersion, "v8.0", "TargetFrameworkVersion should be v8.0"); Assert.AreEqual(androidTooling.AndroidApiLevelName, "26", "AndroidApiLevelName should be 26"); Assert.NotNull(resolveSdks.ReferenceAssemblyPaths, "ReferenceAssemblyPaths should not be null."); Assert.AreEqual(resolveSdks.ReferenceAssemblyPaths.Length, 1, "ReferenceAssemblyPaths should have 1 entry."); Assert.AreEqual(resolveSdks.ReferenceAssemblyPaths[0], Path.Combine(referencePath, "MonoAndroid"), $"ReferenceAssemblyPaths should be {Path.Combine (referencePath, "MonoAndroid")}."); var expected = Path.Combine(Root); Assert.AreEqual(resolveSdks.MonoAndroidToolsPath, expected, $"MonoAndroidToolsPath should be {expected}"); expected += Path.DirectorySeparatorChar; if (resolveSdks.MonoAndroidBinPath != expected) { //For non-Windows platforms, remove a directory such as "Darwin", MonoAndroidBinPath also has a trailing / var binPath = Path.GetDirectoryName(Path.GetDirectoryName(resolveSdks.MonoAndroidBinPath)) + Path.DirectorySeparatorChar; Assert.AreEqual(binPath, expected, $"MonoAndroidBinPath should be {expected}"); } Assert.AreEqual(resolveSdks.AndroidSdkPath, androidSdkPath, $"AndroidSdkPath should be {androidSdkPath}"); Assert.AreEqual(resolveSdks.JavaSdkPath, javaPath, $"JavaSdkPath should be {javaPath}"); expected = Path.Combine(androidSdkPath, "build-tools", "26.0.3"); Assert.AreEqual(androidTooling.AndroidSdkBuildToolsPath, expected, $"AndroidSdkBuildToolsPath should be {expected}"); Assert.AreEqual(androidTooling.AndroidSdkBuildToolsBinPath, expected, "AndroidSdkBuildToolsBinPath should be {expected}"); Assert.AreEqual(androidTooling.ZipAlignPath, expected, "ZipAlignPath should be {expected}"); Assert.AreEqual(androidTooling.AndroidSequencePointsMode, "None", "AndroidSequencePointsMode should be None"); expected = Path.Combine(androidSdkPath, "tools"); Assert.AreEqual(androidTooling.LintToolPath, expected, $"LintToolPath should be {expected}"); expected = Path.Combine(androidSdkPath, "build-tools", "26.0.3", "lib", "apksigner.jar"); Assert.AreEqual(androidTooling.ApkSignerJar, expected, $"ApkSignerJar should be {expected}"); Assert.AreEqual(androidTooling.AndroidUseApkSigner, false, "AndroidUseApkSigner should be false"); Assert.AreEqual(validateJavaVersion.JdkVersion, "1.8.0", "JdkVersion should be 1.8.0"); Assert.AreEqual(validateJavaVersion.MinimumRequiredJdkVersion, "1.8", "MinimumRequiredJdkVersion should be 1.8"); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void CheckSignApk([Values(true, false)] bool useApkSigner, [Values(true, false)] bool perAbiApk) { string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".bat" : ""; var foundApkSigner = Directory.EnumerateDirectories(Path.Combine(AndroidSdkPath, "build-tools")).Any(dir => Directory.EnumerateFiles(dir, "apksigner" + ext).Any()); if (useApkSigner && !foundApkSigner) { Assert.Ignore("Skipping test. Required build-tools verison which contains apksigner is not installed."); } string keyfile = Path.Combine(Root, "temp", TestName, "release.keystore"); if (File.Exists(keyfile)) { File.Delete(keyfile); } var androidSdk = new AndroidSdkInfo((level, message) => { }, AndroidSdkPath, AndroidNdkPath); string keyToolPath = Path.Combine(androidSdk.JavaSdkPath, "bin"); var engine = new MockBuildEngine(Console.Out); string pass = "******"; var task = new AndroidCreateDebugKey { BuildEngine = engine, KeyStore = keyfile, StorePass = pass, KeyAlias = "releasestore", KeyPass = pass, KeyAlgorithm = "RSA", Validity = 30, StoreType = "pkcs12", Command = "-genkeypair", ToolPath = keyToolPath, }; Assert.IsTrue(task.Execute(), "Task should have succeeded."); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; if (useApkSigner) { proj.SetProperty("AndroidUseApkSigner", "true"); } else { proj.RemoveProperty("AndroidUseApkSigner"); } proj.SetProperty(proj.ReleaseProperties, "AndroidKeyStore", "True"); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyStore", keyfile); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyAlias", "releasestore"); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyPass", pass); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningStorePass", pass); proj.SetProperty(proj.ReleaseProperties, KnownProperties.AndroidCreatePackagePerAbi, perAbiApk); proj.SetAndroidSupportedAbis("armeabi-v7a", "x86"); using (var b = CreateApkBuilder(Path.Combine("temp", TestContext.CurrentContext.Test.Name))) { var bin = Path.Combine(Root, b.ProjectDirectory, proj.OutputPath); Assert.IsTrue(b.Build(proj), "First build failed"); Assert.IsTrue(StringAssertEx.ContainsText(b.LastBuildOutput, " 0 Warning(s)"), "First build should not contain warnings! Contains\n" + string.Join("\n", b.LastBuildOutput.Where(line => line.Contains("warning")))); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } var item = proj.AndroidResources.First(x => x.Include() == "Resources\\values\\Strings.xml"); item.TextContent = () => proj.StringsXml.Replace("${PROJECT_NAME}", "Foo"); item.Timestamp = null; Assert.IsTrue(b.Build(proj), "Second build failed"); Assert.IsTrue(StringAssertEx.ContainsText(b.LastBuildOutput, " 0 Warning(s)"), "Second build should not contain warnings! Contains\n" + string.Join("\n", b.LastBuildOutput.Where(line => line.Contains("warning")))); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } } }
public void CheckClassIsNotReplacedWithMd5() { var path = Path.Combine(Root, "temp", "CheckClassIsNotReplacedWithMd5"); Directory.CreateDirectory(path); var resPath = Path.Combine(path, "res"); Directory.CreateDirectory(Path.Combine(resPath, "layout")); File.WriteAllText(Path.Combine(resPath, "layout", "main.xml"), @"<?xml version='1.0' ?> <LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'> <ClassLibrary1.CustomView xmlns:android='http://schemas.android.com/apk/res/android' /> <classLibrary1.CustomView xmlns:android='http://schemas.android.com/apk/res/android' /> <fragment android:name='classLibrary1.CustomView' /> <fragment class='ClassLibrary1.CustomView' /> </LinearLayout> "); var errors = new List <BuildErrorEventArgs> (); IBuildEngine engine = new MockBuildEngine(TestContext.Out, errors); var task = new ConvertResourcesCases { BuildEngine = engine }; task.ResourceDirectories = new ITaskItem [] { new TaskItem(resPath), }; task.AcwMapFile = Path.Combine(path, "acwmap.txt"); task.CustomViewMapFile = Path.Combine(path, "classmap.txt"); File.WriteAllLines(task.AcwMapFile, new string [] { "ClassLibrary1.CustomView;md5d6f7135293df7527c983d45d07471c5e.CustomTextView", "classlibrary1.CustomView;md5d6f7135293df7527c983d45d07471c5e.CustomTextView", }); Assert.IsTrue(task.Execute(), "Task should have executed successfully"); if (IsWindows) { // Causes an NRE resPath = resPath.ToUpperInvariant(); } var custom = new ConvertCustomView() { BuildEngine = engine, CustomViewMapFile = task.CustomViewMapFile, AcwMapFile = task.AcwMapFile, ResourceDirectories = new ITaskItem [] { new TaskItem(resPath), }, }; Assert.IsFalse(custom.Execute(), "Task should have executed successfully"); var output = File.ReadAllText(Path.Combine(resPath, "layout", "main.xml")); StringAssert.Contains("md5d6f7135293df7527c983d45d07471c5e.CustomTextView", output, "md5d6f7135293df7527c983d45d07471c5e.CustomTextView should exist in the main.xml"); StringAssert.DoesNotContain("ClassLibrary1.CustomView", output, "ClassLibrary1.CustomView should have been replaced."); StringAssert.Contains("classLibrary1.CustomView", output, "classLibrary1.CustomView should have been replaced."); Assert.AreEqual(1, errors.Count, "One Error should have been raised."); Assert.AreEqual("XA1002", errors [0].Code, "XA1002 should have been raised."); var expected = Path.Combine("Resources", "layout", "main.xml"); Assert.AreEqual(expected, errors [0].File, $"Error should have the \"{expected}\" path. But contained \"{errors [0].File}\""); Assert.IsFalse(custom.Execute(), "Task should have executed successfully"); var secondOutput = File.ReadAllText(Path.Combine(resPath, "layout", "main.xml")); StringAssert.AreEqualIgnoringCase(output, secondOutput, "Files should not have changed."); Directory.Delete(path, recursive: true); }
public void Aapt2CompileFixesUpErrors() { var path = Path.Combine(Root, "temp", "Aapt2CompileFixesUpErrors"); Directory.CreateDirectory(path); var resPath = Path.Combine(path, "res"); var archivePath = Path.Combine(path, "flata"); var flatFilePath = Path.Combine(path, "flat"); Directory.CreateDirectory(resPath); Directory.CreateDirectory(archivePath); Directory.CreateDirectory(Path.Combine(resPath, "values")); Directory.CreateDirectory(Path.Combine(resPath, "layout")); File.WriteAllText(Path.Combine(resPath, "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo'>foo</string</resources>"); File.WriteAllText(Path.Combine(resPath, "layout", "main.xml"), @"<?xml version='1.0' ?> <LinearLayout xmlns:android='http://schemas.android.com/apk/res/android' android:orientation='vertical' android:layout_width='fill_parent' android:layout_height='fill_parent' > <Button android:id='@+id/myButton' android:layout_width='fill_parent' android:layout_height='wrap_content' android:text='@string/hello' /> </LinearLayout> "); var errors = new List <BuildErrorEventArgs> (); var engine = new MockBuildEngine(TestContext.Out, errors); var directorySeperator = Path.DirectorySeparatorChar; var current = Directory.GetCurrentDirectory(); try { Directory.SetCurrentDirectory(path); MonoAndroidHelper.SaveResourceCaseMap(engine, new Dictionary <string, string> { { $"layout{directorySeperator}main.axml", $"Layout{directorySeperator}Main.xml" }, { $"values{directorySeperator}strings.xml", $"Values{directorySeperator}Strings.xml" }, }); var task = new Aapt2Compile { BuildEngine = engine, ToolPath = GetPathToAapt2(), ResourceDirectories = new ITaskItem [] { new TaskItem(resPath, new Dictionary <string, string> () { { "ResourceDirectory", resPath }, } ) }, FlatArchivesDirectory = archivePath, FlatFilesDirectory = flatFilePath, }; Assert.False(task.Execute(), "task should not have succeeded."); } finally { Directory.SetCurrentDirectory(current); } Assert.AreEqual(2, errors.Count, $"Two Error should have been raised. {string.Join (" ", errors.Select (e => e.Message))}"); Assert.AreEqual($"Resources{directorySeperator}Values{directorySeperator}Strings.xml", errors[0].File, $"`values{directorySeperator}strings.xml` should have been replaced with `Resources{directorySeperator}Values{directorySeperator}Strings.xml`"); Assert.AreEqual($"Resources{directorySeperator}Values{directorySeperator}Strings.xml", errors [1].File, $"`values{directorySeperator}strings.xml` should have been replaced with `Resources{directorySeperator}Values{directorySeperator}Strings.xml`"); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void CollectNonEmptyDirectoriesTest() { var path = Path.Combine(Root, "temp", TestName); Directory.CreateDirectory(path); var resPath = Path.Combine(path, "res"); var archivePath = Path.Combine(path, "flata"); var flatFilePath = Path.Combine(path, "flat"); var resizerPath = Path.Combine(path, "resizer"); Directory.CreateDirectory(resPath); Directory.CreateDirectory(resizerPath); Directory.CreateDirectory(Path.Combine(path, "stamps")); Directory.CreateDirectory(archivePath); Directory.CreateDirectory(flatFilePath); Directory.CreateDirectory(Path.Combine(resPath, "values")); Directory.CreateDirectory(Path.Combine(resPath, "layout")); Directory.CreateDirectory(Path.Combine(resizerPath, "drawable")); File.WriteAllText(Path.Combine(resPath, "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo'>foo</string></resources>"); File.WriteAllText(Path.Combine(resPath, "layout", "main.xml"), @"<?xml version='1.0' ?><LinearLayout xmlns:android='http://schemas.android.com/apk/res/android' />"); File.WriteAllText(Path.Combine(resizerPath, "drawable", "icon.xml"), @"<?xml version='1.0' ?> <vector xmlns:android=""http://schemas.android.com/apk/res/android"" android:height=""64dp"" android:width=""64dp"" android:viewportHeight=""600"" android:viewportWidth=""600"" > <group android:name=""rotationGroup"" android:pivotX=""300.0"" android:pivotY=""300.0"" android:rotation=""45.0"" > <path android:name=""vectorPath"" android:fillColor=""#000000"" android:pathData=""M300,70 l 0,-70 70,70 0,0 -70,70z"" /> </group> </vector>"); var libPath = Path.Combine(path, "lp"); Directory.CreateDirectory(libPath); Directory.CreateDirectory(Path.Combine(libPath, "0", "res", "values")); Directory.CreateDirectory(Path.Combine(libPath, "1", "res", "values")); File.WriteAllText(Path.Combine(libPath, "0", "res", "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo1'>foo1</string></resources>"); File.WriteAllText(Path.Combine(libPath, "1", "res", "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo2'>foo2</string></resources>"); File.WriteAllText(Path.Combine(path, "AndroidManifest.xml"), @"<?xml version='1.0' ?><manifest xmlns:android='http://schemas.android.com/apk/res/android' package='Foo.Foo' />"); File.WriteAllText(Path.Combine(path, "foo.map"), @"a\nb"); var errors = new List <BuildErrorEventArgs> (); IBuildEngine engine = new MockBuildEngine(TestContext.Out, errors); var task = new CollectNonEmptyDirectories { BuildEngine = engine, Directories = new ITaskItem[] { new TaskItem(resPath, new Dictionary <string, string> { { "FilesCache", Path.Combine(path, "files.cache") }, }), new TaskItem(resizerPath), new TaskItem(Path.Combine(libPath, "0", "res"), new Dictionary <string, string> { { "AndroidSkipResourceProcessing", "True" }, { "StampFile", "0.stamp" }, }), new TaskItem(Path.Combine(libPath, "1", "res")), }, LibraryProjectIntermediatePath = libPath, StampDirectory = Path.Combine(path, "stamps"), }; Assert.True(task.Execute(), $"task should have succeeded. {string.Join (";", errors.Select (x => x.Message))}"); Assert.AreEqual(4, task.Output.Length, "Output should have 4 items in it."); Assert.AreEqual(5, task.LibraryResourceFiles.Length, "Output should have 5 items in it."); Assert.AreEqual("layout_main.xml.flat", task.LibraryResourceFiles[0].GetMetadata("_FlatFile")); Assert.AreEqual("values_strings.arsc.flat", task.LibraryResourceFiles[1].GetMetadata("_FlatFile")); Assert.AreEqual("0.flata", task.LibraryResourceFiles[3].GetMetadata("_FlatFile")); Assert.AreEqual("values_strings.arsc.flat", task.LibraryResourceFiles[4].GetMetadata("_FlatFile")); foreach (var item in task.Output) { Assert.IsNotNull(item.GetMetadata("FilesCache"), "FilesCache should have been set"); var cacheFile = item.GetMetadata("FilesCache"); FileAssert.Exists(cacheFile, $"{cacheFile} should have been created."); } }
public void Aapt2Link([Values(true, false)] bool compilePerFile) { var path = Path.Combine(Root, "temp", TestName); Directory.CreateDirectory(path); var resPath = Path.Combine(path, "res"); var archivePath = Path.Combine(path, "flata"); var flatFilePath = Path.Combine(path, "flat"); Directory.CreateDirectory(resPath); Directory.CreateDirectory(archivePath); Directory.CreateDirectory(flatFilePath); Directory.CreateDirectory(Path.Combine(resPath, "values")); Directory.CreateDirectory(Path.Combine(resPath, "layout")); File.WriteAllText(Path.Combine(resPath, "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo'>foo</string></resources>"); File.WriteAllText(Path.Combine(resPath, "layout", "main.xml"), @"<?xml version='1.0' ?><LinearLayout xmlns:android='http://schemas.android.com/apk/res/android' />"); var libPath = Path.Combine(path, "lp"); Directory.CreateDirectory(libPath); Directory.CreateDirectory(Path.Combine(libPath, "0", "res", "values")); Directory.CreateDirectory(Path.Combine(libPath, "1", "res", "values")); File.WriteAllText(Path.Combine(libPath, "0", "res", "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo1'>foo1</string></resources>"); File.WriteAllText(Path.Combine(libPath, "1", "res", "values", "strings.xml"), @"<?xml version='1.0' ?><resources><string name='foo2'>foo2</string></resources>"); File.WriteAllText(Path.Combine(path, "AndroidManifest.xml"), @"<?xml version='1.0' ?><manifest xmlns:android='http://schemas.android.com/apk/res/android' package='Foo.Foo' />"); File.WriteAllText(Path.Combine(path, "foo.map"), @"a\nb"); var errors = new List <BuildErrorEventArgs> (); var warnings = new List <BuildWarningEventArgs> (); IBuildEngine engine = new MockBuildEngine(TestContext.Out, errors, warnings); var archives = new List <ITaskItem>(); if (compilePerFile) { foreach (var file in Directory.EnumerateFiles(resPath, "*.*", SearchOption.AllDirectories)) { CallAapt2Compile(engine, file, archivePath, flatFilePath); } } else { CallAapt2Compile(engine, resPath, archivePath, flatFilePath); } CallAapt2Compile(engine, Path.Combine(libPath, "0", "res"), archivePath, flatFilePath, archives); CallAapt2Compile(engine, Path.Combine(libPath, "1", "res"), archivePath, flatFilePath, archives); List <ITaskItem> items = new List <ITaskItem> (); if (compilePerFile) { // collect all the flat archives foreach (var file in Directory.EnumerateFiles(flatFilePath, "*.flat", SearchOption.AllDirectories)) { items.Add(new TaskItem(file)); } } int platform = AndroidSdkResolver.GetMaxInstalledPlatform(); var outputFile = Path.Combine(path, "resources.apk"); var task = new Aapt2Link { BuildEngine = engine, ToolPath = GetPathToAapt2(), ResourceDirectories = new ITaskItem [] { new TaskItem(resPath) }, ManifestFiles = new ITaskItem [] { new TaskItem(Path.Combine(path, "AndroidManifest.xml")) }, AdditionalResourceArchives = !compilePerFile?archives.ToArray() : null, CompiledResourceFlatArchive = !compilePerFile ? new TaskItem(Path.Combine(archivePath, "compiled.flata")) : null, CompiledResourceFlatFiles = compilePerFile ? items.ToArray() : null, OutputFile = outputFile, AssemblyIdentityMapFile = Path.Combine(path, "foo.map"), JavaPlatformJarPath = Path.Combine(AndroidSdkPath, "platforms", $"android-{platform}", "android.jar"), }; Assert.True(task.Execute(), $"task should have succeeded. {string.Join (";", errors.Select (x => x.Message))}"); Assert.AreEqual(0, errors.Count, "There should be no errors."); Assert.LessOrEqual(0, warnings.Count, "There should be 0 warnings."); Assert.True(File.Exists(outputFile), $"{outputFile} should have been created."); using (var apk = ZipHelper.OpenZip(outputFile)) { Assert.AreEqual(3, apk.EntryCount, $"{outputFile} should have 3 entries."); } Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void TestResolveToolsExists() { List <BuildErrorEventArgs> errors = new List <BuildErrorEventArgs>(); List <BuildMessageEventArgs> messages = new List <BuildMessageEventArgs>(); var path = Path.Combine("temp", TestName); if (Directory.Exists(Path.Combine(Root, path))) { Directory.Delete(Path.Combine(Root, path), recursive: true); } var engine = new MockBuildEngine(TestContext.Out, errors: errors, messages: messages); var outPath = TestEnvironment.IsRunningOnCI ? TestEnvironment.MonoAndroidToolsDirectory : new Uri(Path.Combine(XABuildPaths.PrefixDirectory, "lib", "xamarin.android", "xbuild", "Xamarin", "Android")).LocalPath; var frameworksPath = TestEnvironment.IsRunningOnCI ? Path.Combine(TestEnvironment.MonoAndroidFrameworkDirectory, "v1.0") : new Uri(Path.Combine(XABuildPaths.PrefixDirectory, "lib", "xamarin.android", "xbuild-frameworks", "MonoAndroid", "v1.0")).LocalPath; var androidSdk = CreateFauxAndroidSdkDirectory(Path.Combine(path, "Sdk"), "24.0.1", new[] { new ApiInfo { Id = "23", Level = 23, Name = "Marshmallow", FrameworkVersion = "v6.0", Stable = true }, new ApiInfo { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true }, new ApiInfo { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true }, new ApiInfo { Id = "28", Level = 28, Name = "Pie", FrameworkVersion = "v9.0", Stable = true }, }); //var androidNdk = CreateFauxAndroidNdkDirectory (Path.Combine (path, "Ndk")); var javaSdk = CreateFauxJavaSdkDirectory(Path.Combine(path, "Java"), "1.8.0", out string javaExe, out string javacExe); var task = new ResolveXamarinAndroidTools() { BuildEngine = engine, AndroidNdkPath = null, AndroidSdkPath = androidSdk, JavaSdkPath = javaSdk, MonoAndroidToolsPath = outPath, ReferenceAssemblyPaths = new string[] { frameworksPath, }, }; Assert.True(task.Execute(), "Task should have completed successfully."); Assert.AreEqual(0, errors.Count, "No Errors should have been raised"); var expected = $" Found FrameworkPath at {Path.GetFullPath (frameworksPath)}"; var firstTaskExecMessages = messages.Select(x => x.Message)?.ToList(); Assert.IsNotNull(firstTaskExecMessages, "First execution did not contain any messages!"); CollectionAssert.Contains(firstTaskExecMessages, expected); CollectionAssert.DoesNotContain(firstTaskExecMessages, " Using cached AndroidSdk values"); CollectionAssert.DoesNotContain(firstTaskExecMessages, " Using cached MonoDroidSdk values"); Assert.True(task.Execute(), "Task should have completed successfully."); Assert.AreEqual(0, errors.Count, "No Errors should have been raised"); var secondTaskExecMessages = messages.Select(x => x.Message)?.ToList(); Assert.IsNotNull(secondTaskExecMessages, "Second execution did not contain any messages!"); CollectionAssert.Contains(secondTaskExecMessages, expected); CollectionAssert.Contains(secondTaskExecMessages, " Using cached AndroidSdk values"); CollectionAssert.Contains(secondTaskExecMessages, " Using cached MonoDroidSdk values"); }
public void ResolveSdkTiming() { var path = Path.Combine("temp", TestName); var androidSdkPath = CreateFauxAndroidSdkDirectory(Path.Combine(path, "android-sdk"), "26.0.3"); string javaExe = string.Empty; var javaPath = CreateFauxJavaSdkDirectory(Path.Combine(path, "jdk"), "1.8.0", out javaExe); var referencePath = CreateFauxReferencesDirectory(Path.Combine(path, "references"), new ApiInfo [] { new ApiInfo() { Id = "26", Level = 26, Name = "Oreo", FrameworkVersion = "v8.0", Stable = true }, new ApiInfo() { Id = "27", Level = 27, Name = "Oreo", FrameworkVersion = "v8.1", Stable = true }, }); IBuildEngine engine = new MockBuildEngine(TestContext.Out); var task = new ResolveSdks { BuildEngine = engine }; task.AndroidSdkPath = androidSdkPath; task.AndroidNdkPath = androidSdkPath; task.JavaSdkPath = javaPath; task.TargetFrameworkVersion = "v8.0"; task.AndroidSdkBuildToolsVersion = "26.0.3"; task.BuildingInsideVisualStudio = "true"; task.UseLatestAndroidPlatformSdk = false; task.AotAssemblies = false; task.LatestSupportedJavaVersion = "1.8.0"; task.MinimumSupportedJavaVersion = "1.7.0"; task.ReferenceAssemblyPaths = new string [] { Path.Combine(referencePath, "MonoAndroid"), }; task.CacheFile = Path.Combine(Root, path, "sdk.xml"); task.SequencePointsMode = "None"; task.JavaToolExe = javaExe; var start = DateTime.UtcNow; Assert.IsTrue(task.Execute()); var executionTime = DateTime.UtcNow - start; Assert.LessOrEqual(executionTime, TimeSpan.FromSeconds(1), "Task should not take more than 1 second to run."); Assert.AreEqual(task.AndroidApiLevel, "26", "AndroidApiLevel should be 26"); Assert.AreEqual(task.TargetFrameworkVersion, "v8.0", "TargetFrameworkVersion should be v8.0"); Assert.AreEqual(task.AndroidApiLevelName, "26", "AndroidApiLevelName should be 26"); Assert.AreEqual(task.SupportedApiLevel, "26", "SupportedApiLevel should be 26"); Assert.NotNull(task.ReferenceAssemblyPaths, "ReferenceAssemblyPaths should not be null."); Assert.AreEqual(task.ReferenceAssemblyPaths.Length, 1, "ReferenceAssemblyPaths should have 1 entry."); Assert.AreEqual(task.ReferenceAssemblyPaths[0], Path.Combine(referencePath, "MonoAndroid"), $"ReferenceAssemblyPaths should be {Path.Combine (referencePath, "MonoAndroid")}."); var expected = Path.Combine(Root); Assert.AreEqual(task.MonoAndroidToolsPath, expected, $"MonoAndroidToolsPath should be {expected}"); expected = Path.Combine(Root, "Darwin" + Path.DirectorySeparatorChar); Assert.AreEqual(task.MonoAndroidBinPath, expected, $"MonoAndroidBinPath should be {expected}"); Assert.AreEqual(task.MonoAndroidIncludePath, null, "MonoAndroidIncludePath should be null"); //Assert.AreEqual (task.AndroidNdkPath, "26", "AndroidNdkPath should be 26"); Assert.AreEqual(task.AndroidSdkPath, androidSdkPath, $"AndroidSdkPath should be {androidSdkPath}"); Assert.AreEqual(task.JavaSdkPath, javaPath, $"JavaSdkPath should be {javaPath}"); expected = Path.Combine(androidSdkPath, "build-tools", "26.0.3"); Assert.AreEqual(task.AndroidSdkBuildToolsPath, expected, $"AndroidSdkBuildToolsPath should be {expected}"); Assert.AreEqual(task.AndroidSdkBuildToolsBinPath, expected, "AndroidSdkBuildToolsBinPath should be {expected}"); Assert.AreEqual(task.ZipAlignPath, expected, "ZipAlignPath should be {expected}"); Assert.AreEqual(task.AndroidSequencePointsMode, "None", "AndroidSequencePointsMode should be None"); expected = Path.Combine(androidSdkPath, "tools"); Assert.AreEqual(task.LintToolPath, expected, $"LintToolPath should be {expected}"); expected = Path.Combine(androidSdkPath, "build-tools", "26.0.3", "lib", "apksigner.jar"); Assert.AreEqual(task.ApkSignerJar, expected, $"ApkSignerJar should be {expected}"); Assert.AreEqual(task.AndroidUseApkSigner, false, "AndroidUseApkSigner should be false"); Assert.AreEqual(task.JdkVersion, "1.8.0", "JdkVersion should be 1.8.0"); Assert.AreEqual(task.MinimumRequiredJdkVersion, "1.8", "MinimumRequiredJdkVersion should be 1.8"); Directory.Delete(Path.Combine(Root, path), recursive: true); }
public void CheckSignApk([Values(true, false)] bool useApkSigner, [Values(true, false)] bool perAbiApk) { string ext = Environment.OSVersion.Platform != PlatformID.Unix ? ".bat" : ""; var foundApkSigner = Directory.EnumerateDirectories(Path.Combine(AndroidSdkPath, "build-tools")).Any(dir => Directory.EnumerateFiles(dir, "apksigner" + ext).Any()); if (useApkSigner && !foundApkSigner) { Assert.Ignore("Skipping test. Required build-tools verison which contains apksigner is not installed."); } string keyfile = Path.Combine(Root, "temp", TestName, "release.keystore"); if (File.Exists(keyfile)) { File.Delete(keyfile); } string keyToolPath = Path.Combine(AndroidSdkResolver.GetJavaSdkPath(), "bin"); var engine = new MockBuildEngine(Console.Out); string pass = "******"; string alias = "release store"; var task = new AndroidCreateDebugKey { BuildEngine = engine, KeyStore = keyfile, StorePass = pass, KeyAlias = alias, KeyPass = pass, KeyAlgorithm = "RSA", Validity = 30, StoreType = "pkcs12", Command = "-genkeypair", ToolPath = keyToolPath, }; Assert.IsTrue(task.Execute(), "Task should have succeeded."); var proj = new XamarinAndroidApplicationProject() { IsRelease = true, }; proj.SetProperty(proj.ReleaseProperties, "AndroidUseApkSigner", useApkSigner); proj.SetProperty(proj.ReleaseProperties, "AndroidKeyStore", "True"); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyStore", keyfile); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyAlias", alias); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningKeyPass", Uri.EscapeDataString(pass)); proj.SetProperty(proj.ReleaseProperties, "AndroidSigningStorePass", Uri.EscapeDataString(pass)); proj.SetProperty(proj.ReleaseProperties, KnownProperties.AndroidCreatePackagePerAbi, perAbiApk); if (perAbiApk) { proj.SetAndroidSupportedAbis("armeabi-v7a", "x86", "arm64-v8a", "x86_64"); } else { proj.SetAndroidSupportedAbis("armeabi-v7a", "x86"); } using (var b = CreateApkBuilder(Path.Combine("temp", TestContext.CurrentContext.Test.Name))) { var bin = Path.Combine(Root, b.ProjectDirectory, proj.OutputPath); Assert.IsTrue(b.Build(proj), "First build failed"); b.AssertHasNoWarnings(); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } // Make sure the APKs have unique version codes if (perAbiApk) { int armManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "armeabi-v7a", "AndroidManifest.xml")); int x86ManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "x86", "AndroidManifest.xml")); int arm64ManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "arm64-v8a", "AndroidManifest.xml")); int x86_64ManifestCode = GetVersionCodeFromIntermediateManifest(Path.Combine(Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "x86_64", "AndroidManifest.xml")); var versionList = new List <int> { armManifestCode, x86ManifestCode, arm64ManifestCode, x86_64ManifestCode }; Assert.True(versionList.Distinct().Count() == versionList.Count, $"APK version codes were not unique - armeabi-v7a: {armManifestCode}, x86: {x86ManifestCode}, arm64-v8a: {arm64ManifestCode}, x86_64: {x86_64ManifestCode}"); } var item = proj.AndroidResources.First(x => x.Include() == "Resources\\values\\Strings.xml"); item.TextContent = () => proj.StringsXml.Replace("${PROJECT_NAME}", "Foo"); item.Timestamp = null; Assert.IsTrue(b.Build(proj), "Second build failed"); b.AssertHasNoWarnings(); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles(bin, "*-Signed.apk")) { using (var zip = ZipHelper.OpenZip(apk)) { Assert.IsTrue(zip.Any(e => e.FullName == "META-INF/MANIFEST.MF"), $"APK file `{apk}` is not signed! It is missing `META-INF/MANIFEST.MF`."); } } } int GetVersionCodeFromIntermediateManifest(string manifestFilePath) { var doc = XDocument.Load(manifestFilePath); var versionCode = doc.Descendants() .Where(e => e.Name == "manifest") .Select(m => m.Attribute("{http://schemas.android.com/apk/res/android}versionCode")).FirstOrDefault(); if (!int.TryParse(versionCode?.Value, out int parsedCode)) { Assert.Fail($"Unable to parse 'versionCode' value from manifest content: {File.ReadAllText (manifestFilePath)}."); } return(parsedCode); } }