[tests] Add a HotRestart version of the BundleStructure test.
This commit is contained in:
Родитель
d6195febf1
Коммит
8e6104c497
|
@ -2,6 +2,8 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -239,6 +241,31 @@ namespace Xamarin.Tests {
|
|||
}
|
||||
}
|
||||
|
||||
public static bool TryFindPropertyValue (string binlog, string property, [NotNullWhen (true)] out string? value)
|
||||
{
|
||||
value = null;
|
||||
|
||||
var reader = new BinLogReader ();
|
||||
foreach (var record in reader.ReadRecords (binlog)) {
|
||||
var args = record?.Args;
|
||||
if (args is null)
|
||||
continue;
|
||||
if (args is PropertyInitialValueSetEventArgs pivsea) {
|
||||
if (string.Equals (property, pivsea.PropertyName, StringComparison.OrdinalIgnoreCase))
|
||||
value = pivsea.PropertyValue;
|
||||
} else if (args is PropertyReassignmentEventArgs prea) {
|
||||
if (string.Equals (property, prea.PropertyName, StringComparison.OrdinalIgnoreCase))
|
||||
value = prea.NewValue;
|
||||
} else if (args is ProjectEvaluationFinishedEventArgs pefea) {
|
||||
var dict = pefea.Properties as IDictionary<string, string>;
|
||||
if (dict is not null && dict.TryGetValue (property, out var pvalue))
|
||||
value = pvalue;
|
||||
}
|
||||
}
|
||||
|
||||
return value is not null;
|
||||
}
|
||||
|
||||
// Returns a diagnostic build log as a string
|
||||
public static string PrintToString (string path)
|
||||
{
|
||||
|
|
|
@ -323,8 +323,9 @@
|
|||
<!-- The bindings-framework-test project contains (next to the binding assembly):
|
||||
*.resources/* files for iOS and tvOS
|
||||
*.resources.zip for macOS and Mac Catalyst
|
||||
It's only included when building on a Mac (remotely or locally), because we don't support building binding frameworks yet from Windows (without being connected to a Mac).
|
||||
-->
|
||||
<ProjectReference Include="$(RootTestsDirectory)/bindings-framework-test/dotnet/$(_PlatformName)/bindings-framework-test.csproj" />
|
||||
<ProjectReference Include="$(RootTestsDirectory)/bindings-framework-test/dotnet/$(_PlatformName)/bindings-framework-test.csproj" Condition="'$(IsMacEnabled)' == 'true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Adding resources using a custom target -->
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Xamarin.Tests {
|
|||
[TestFixture]
|
||||
public class BundleStructureTest : TestBaseClass {
|
||||
// Returns true if the assembly name is _any_ of our platform assemblies (Microsoft.iOS/tvOS/macOS/MacCatalyst/watchOS.dll)
|
||||
bool IsPlatformAssembly (string assemblyName)
|
||||
static bool IsPlatformAssembly (string assemblyName)
|
||||
{
|
||||
if (assemblyName.EndsWith (".dll", StringComparison.Ordinal) || assemblyName.EndsWith (".pdb", StringComparison.Ordinal))
|
||||
assemblyName = Path.GetFileNameWithoutExtension (assemblyName);
|
||||
|
@ -18,19 +18,42 @@ namespace Xamarin.Tests {
|
|||
return false;
|
||||
}
|
||||
|
||||
void CheckAppBundleContents (ApplePlatform platform, string appPath, string [] runtimeIdentifiers, CodeSignature isSigned, bool isReleaseBuild)
|
||||
public static List<string> Find (string appPath)
|
||||
{
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
|
||||
var dir = new DirectoryInfo (appPath);
|
||||
var managedFiles = dir.GetFileSystemInfos ("*", SearchOption.AllDirectories)
|
||||
.Select (v => v.FullName)
|
||||
.Select (v => v.Substring (appPath.Length + 1))
|
||||
.Order ()
|
||||
.ToList ();
|
||||
return managedFiles;
|
||||
}
|
||||
|
||||
// Directory.GetFileSystemEntries will enter symlink directories and iterate inside :/
|
||||
var output = AssertExecute ("find", appPath);
|
||||
var allFiles = output.ToString ()
|
||||
.Split ('\n', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where (v => v.Length > appPath.Length)
|
||||
.Select (v => v.Substring (appPath.Length + 1))
|
||||
.Order ()
|
||||
.ToList ();
|
||||
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
internal static void CheckAppBundleContents (ApplePlatform platform, string appPath, string [] runtimeIdentifiers, CodeSignature isSigned, bool isReleaseBuild)
|
||||
{
|
||||
Console.WriteLine ($"App bundle: {appPath}");
|
||||
Assert.That (appPath, Does.Exist, "App bundle existence");
|
||||
var output = AssertExecute ("find", appPath);
|
||||
var allFiles = Find (appPath);
|
||||
CheckAppBundleContents (platform, allFiles, runtimeIdentifiers, isSigned, isReleaseBuild, appPath);
|
||||
}
|
||||
|
||||
internal static void CheckAppBundleContents (ApplePlatform platform, IEnumerable<string> allFiles, string [] runtimeIdentifiers, CodeSignature isSigned, bool isReleaseBuild, string? appPath = null)
|
||||
{
|
||||
var isCoreCLR = platform == ApplePlatform.MacOSX;
|
||||
var includeDebugFiles = !isReleaseBuild;
|
||||
var allFiles = output.ToString ().
|
||||
Split ('\n', StringSplitOptions.RemoveEmptyEntries).
|
||||
Where (v => v.Length > appPath.Length).
|
||||
Select (v => v.Substring (appPath.Length + 1)).ToList ();
|
||||
|
||||
// Remove various files we don't care about (for this test) from the list of files in the app bundle.
|
||||
Predicate<string?> predicate = (v) => {
|
||||
|
@ -83,7 +106,7 @@ namespace Xamarin.Tests {
|
|||
return false;
|
||||
};
|
||||
|
||||
allFiles.RemoveAll (predicate);
|
||||
allFiles = allFiles.Where (v => !predicate (v));
|
||||
|
||||
var expectedFiles = new List<string> ();
|
||||
|
||||
|
@ -342,12 +365,14 @@ namespace Xamarin.Tests {
|
|||
Assert.That (unexpectedFiles, Is.Empty, "No unexpected files");
|
||||
Assert.That (missingFiles, Is.Empty, "No missing files");
|
||||
|
||||
AssertDynamicLibraryId (platform, appPath, assemblyDirectory, "libSkipInstallNameTool.dylib");
|
||||
AssertDynamicLibraryId (platform, appPath, assemblyDirectory, "libSkipInstallNameTool.so");
|
||||
AssertLibraryArchitectures (appPath, runtimeIdentifiers);
|
||||
if (appPath is not null) {
|
||||
AssertDynamicLibraryId (platform, appPath, assemblyDirectory, "libSkipInstallNameTool.dylib");
|
||||
AssertDynamicLibraryId (platform, appPath, assemblyDirectory, "libSkipInstallNameTool.so");
|
||||
AssertLibraryArchitectures (appPath, runtimeIdentifiers);
|
||||
}
|
||||
}
|
||||
|
||||
void AssertDynamicLibraryId (ApplePlatform platform, string appPath, string dylibDirectory, string library)
|
||||
static void AssertDynamicLibraryId (ApplePlatform platform, string appPath, string dylibDirectory, string library)
|
||||
{
|
||||
var dylibPath = Path.Combine (appPath, dylibDirectory, library);
|
||||
Assert.That (dylibPath, Does.Exist, "dylib existence");
|
||||
|
@ -673,7 +698,7 @@ namespace Xamarin.Tests {
|
|||
|
||||
}
|
||||
|
||||
void AssertLibraryArchitectures (string appBundle, string [] runtimeIdentifiers)
|
||||
static void AssertLibraryArchitectures (string appBundle, string [] runtimeIdentifiers)
|
||||
{
|
||||
var renderArchitectures = (IEnumerable<Abi> architectures) => {
|
||||
return string.Join (", ",
|
||||
|
|
|
@ -234,13 +234,13 @@ namespace Xamarin.Tests {
|
|||
Assert.That (dSYMDirectory, Does.Exist, "dsym directory");
|
||||
}
|
||||
|
||||
protected string GetNativeExecutable (ApplePlatform platform, string app_directory)
|
||||
protected static string GetNativeExecutable (ApplePlatform platform, string app_directory)
|
||||
{
|
||||
var executableName = Path.GetFileNameWithoutExtension (app_directory);
|
||||
return Path.Combine (app_directory, GetRelativeExecutableDirectory (platform), executableName);
|
||||
}
|
||||
|
||||
protected string GetRelativeExecutableDirectory (ApplePlatform platform)
|
||||
protected static string GetRelativeExecutableDirectory (ApplePlatform platform)
|
||||
{
|
||||
switch (platform) {
|
||||
case ApplePlatform.iOS:
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
// #define TRACE
|
||||
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Xamarin.Tests {
|
||||
|
@ -15,5 +20,146 @@ namespace Xamarin.Tests {
|
|||
{
|
||||
Configuration.IgnoreIfIgnoredPlatform (platform);
|
||||
}
|
||||
|
||||
[TestCase (ApplePlatform.iOS, "ios-arm64")]
|
||||
public void BundleStructureWithHotRestart (ApplePlatform platform, string runtimeIdentifiers)
|
||||
{
|
||||
var project = "BundleStructure";
|
||||
var configuration = "Debug";
|
||||
var tmpdir = Cache.CreateTemporaryDirectory ();
|
||||
|
||||
Configuration.IgnoreIfIgnoredPlatform (platform);
|
||||
|
||||
var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration);
|
||||
var project_dir = Path.GetDirectoryName (Path.GetDirectoryName (project_path))!;
|
||||
Clean (project_path);
|
||||
|
||||
var properties = GetDefaultProperties (runtimeIdentifiers);
|
||||
if (!string.IsNullOrWhiteSpace (configuration))
|
||||
properties ["Configuration"] = configuration;
|
||||
properties ["IsHotRestartBuild"] = "true";
|
||||
properties ["IsHotRestartEnvironmentReady"] = "true";
|
||||
properties ["EnableCodeSigning"] = "false"; // Skip code signing, since that would require making sure we have code signing configured on bots.
|
||||
properties ["_AppIdentifier"] = "placeholder_AppIdentifier"; // This needs to be set to a placeholder value because DetectSigningIdentity usually does it (and we've disabled signing)
|
||||
properties ["_BundleIdentifier"] = "placeholder_BundleIdentifier"; // This needs to be set to a placeholder value because DetectSigningIdentity usually does it (and we've disabled signing)
|
||||
properties ["_IsAppSigned"] = "false";
|
||||
|
||||
// Redirect hot restart output to a place we can control from here
|
||||
var hotRestartOutputDir = Path.Combine (tmpdir, "out");
|
||||
Directory.CreateDirectory (hotRestartOutputDir);
|
||||
properties ["HotRestartSignedAppOutputDir"] = hotRestartOutputDir + Path.DirectorySeparatorChar;
|
||||
var hotRestartAppBundlePath = Path.Combine (tmpdir, "HotRestartAppBundlePath"); // Do not create this directory, it will be created and populated with default contents if it doesn't exist.
|
||||
properties ["HotRestartAppBundlePath"] = hotRestartAppBundlePath; // no trailing directory separator char for this property.
|
||||
var rv = DotNet.AssertBuild (project_path, properties);
|
||||
|
||||
// Find the files in the prebuilt hot restart app
|
||||
var prebuiltAppEntries = Array.Empty<string> ().ToHashSet ();
|
||||
if (BinLog.TryFindPropertyValue (rv.BinLogPath, "MessagingAgentsDirectory", out var preBuiltAppBundleLocation)) {
|
||||
var preBuiltAppBundlePath = Path.Combine (preBuiltAppBundleLocation, "Xamarin.PreBuilt.iOS.app.zip");
|
||||
using var archive = System.IO.Compression.ZipFile.OpenRead (preBuiltAppBundlePath);
|
||||
prebuiltAppEntries = archive
|
||||
.Entries
|
||||
.Select (v => v.FullName)
|
||||
.SelectMany (v => {
|
||||
// This code has two purposes:
|
||||
// 1 - make sure the paths are using the current platform's directory separator char (instead of '/')
|
||||
// 2 - add both files and their containing directories to the list (since we check for directory presence later in this test)
|
||||
var components = v.Split (new char [] { '/', Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var rv = new List<string> ();
|
||||
for (var i = 0; i < components.Length; i++) {
|
||||
rv.Add (Path.Combine (components.Take (i + 1).ToArray ()));
|
||||
}
|
||||
return rv;
|
||||
})
|
||||
.ToHashSet ();
|
||||
|
||||
#if TRACE
|
||||
Console.WriteLine ($"Prebuilt app files:");
|
||||
foreach (var pbf in prebuiltAppEntries)
|
||||
Console.WriteLine ($" {pbf}");
|
||||
#endif
|
||||
} else {
|
||||
Assert.Fail ("Could not find the property 'MessagingAgentsDirectory' in the binlog.");
|
||||
}
|
||||
|
||||
DumpDirContents (appPath);
|
||||
DumpDirContents (tmpdir);
|
||||
|
||||
var hotRestartAppBundleFiles = BundleStructureTest.Find (hotRestartAppBundlePath);
|
||||
|
||||
var payloadFiles = BundleStructureTest.Find (Path.Combine (hotRestartOutputDir, "Payload", "BundleStructure.app"));
|
||||
var contentFiles = BundleStructureTest.Find (Path.Combine (hotRestartOutputDir, "BundleStructure.content"));
|
||||
|
||||
// Exclude most files from the prebuilt hot restart app
|
||||
var excludedPrebuiltAppEntries = prebuiltAppEntries
|
||||
.Where (v => {
|
||||
// We're not excluding some files that are common to all apps.
|
||||
switch (v) {
|
||||
case "Info.plist":
|
||||
case "MonoTouchDebugConfiguration.txt":
|
||||
case "PkgInfo":
|
||||
case "Settings.bundle":
|
||||
case "Settings.bundle\\Root.plist":
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
hotRestartAppBundleFiles = hotRestartAppBundleFiles
|
||||
.Except (excludedPrebuiltAppEntries)
|
||||
.ToList ();
|
||||
|
||||
var merged = hotRestartAppBundleFiles
|
||||
.Union (payloadFiles)
|
||||
.Union (contentFiles)
|
||||
.Where (v => {
|
||||
// remove files in the BundleStructure.content subdirectory
|
||||
if (v.StartsWith ("BundleStructure.content", StringComparison.Ordinal))
|
||||
return false;
|
||||
// hotrestart-specific files
|
||||
if (v == "Extracted")
|
||||
return false;
|
||||
if (v == "Entitlements.plist")
|
||||
return false;
|
||||
if (v == "BundleStructure.hotrestartapp")
|
||||
return false;
|
||||
return true;
|
||||
})
|
||||
.Distinct ()
|
||||
.OrderBy (v => v)
|
||||
.ToList ();
|
||||
|
||||
// The reference to the bindings-framework-test project is skipped on Windows, because we can't build binding projects unless we're connected to a Mac.
|
||||
AddOrAssert (merged, "bindings-framework-test.dll");
|
||||
AddOrAssert (merged, "bindings-framework-test.pdb");
|
||||
AddOrAssert (merged, Path.Combine ("Frameworks", "XTest.framework")); // XTest.framework comes from bindings-framework-test.csproj
|
||||
AddOrAssert (merged, Path.Combine ("Frameworks", "XTest.framework", "Info.plist"));
|
||||
AddOrAssert (merged, Path.Combine ("Frameworks", "XTest.framework", "XTest"));
|
||||
|
||||
// The name of the executable is different.
|
||||
AddOrAssert (merged, project);
|
||||
|
||||
var rids = runtimeIdentifiers.Split (';');
|
||||
BundleStructureTest.CheckAppBundleContents (platform, merged, rids, BundleStructureTest.CodeSignature.None, configuration == "Release");
|
||||
}
|
||||
|
||||
static void AddOrAssert (IList<string> list, string item)
|
||||
{
|
||||
Assert.That (list, Does.Not.Contain (item), $"item {item} already in list.");
|
||||
list.Add (item);
|
||||
}
|
||||
|
||||
static void DumpDirContents (string dir)
|
||||
{
|
||||
#if TRACE
|
||||
if (!Directory.Exists (dir)) {
|
||||
Console.WriteLine ($"The directory {dir} does not exist!");
|
||||
return;
|
||||
}
|
||||
var files = Directory.GetFileSystemEntries (dir, "*", SearchOption.AllDirectories);
|
||||
Console.WriteLine ($"Found {files.Count ()} in {dir}:");
|
||||
foreach (var entry in files.OrderBy (v => v))
|
||||
Console.WriteLine ($" {entry}");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@
|
|||
<Compile Include="..\..\tools\common\SdkVersions.cs">
|
||||
<Link>SdkVersions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\NullableAttributes.cs">
|
||||
<Link>NullableAttributes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AttributeFactoryTests.cs" />
|
||||
<Compile Include="CollectionsExtensionsTests.cs" />
|
||||
<Compile Include="ConstructorArgumentsTests.cs" />
|
||||
|
|
|
@ -97,6 +97,9 @@
|
|||
<Compile Include="..\..\tools\common\StringUtils.cs">
|
||||
<Link>StringUtils.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\NullableAttributes.cs">
|
||||
<Link>NullableAttributes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="src\WarningTests.cs" />
|
||||
<Compile Include="src\Unified45.cs" />
|
||||
<Compile Include="src\System.ServiceModel\Net45.cs" />
|
||||
|
|
|
@ -59,6 +59,9 @@
|
|||
<Compile Include="..\..\common\Tool.cs">
|
||||
<Link>external\Tool.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\tools\common\NullableAttributes.cs">
|
||||
<Link>external\NullableAttributes.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\external\Xamarin.MacDev\Xamarin.MacDev\Xamarin.MacDev.csproj" />
|
||||
|
|
|
@ -58,6 +58,9 @@
|
|||
<Compile Include="..\..\tools\common\StringUtils.cs">
|
||||
<Link>StringUtils.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\..\tools\common\NullableAttributes.cs">
|
||||
<Link>NullableAttributes.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="LinkerTests.cs" />
|
||||
<Compile Include="..\common\ProductTests.cs">
|
||||
<Link>ProductTests.cs</Link>
|
||||
|
|
Загрузка…
Ссылка в новой задаче