[tests] Add a HotRestart version of the BundleStructure test.

This commit is contained in:
Rolf Bjarne Kvinge 2023-02-16 14:30:19 +01:00
Родитель d6195febf1
Коммит 8e6104c497
9 изменённых файлов: 227 добавлений и 16 удалений

Просмотреть файл

@ -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>