Skip to content

Commit

Permalink
[Rge] Use the api definiton for the transformer tests.
Browse files Browse the repository at this point in the history
We provide the apidefinition rsp file for each of the platforms under
test to ensure that we are testing our code against a real world
scenario.
  • Loading branch information
mandel-macaque committed Jan 21, 2025
1 parent 9516bf1 commit d6eaa99
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 50 deletions.
85 changes: 65 additions & 20 deletions tests/common/Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

using Xamarin.Utils;

#nullable disable // until we get around to fixing this file
Expand Down Expand Up @@ -44,6 +44,7 @@ static partial class Configuration {
public static string DOTNET_DIR;

static Version xcode_version;

public static Version XcodeVersion {
get {
if (xcode_version is null)
Expand All @@ -53,6 +54,7 @@ public static Version XcodeVersion {
}

static bool? use_system; // if the system-installed XI/XM should be used instead of the local one.

public static bool UseSystem {
get {
if (!use_system.HasValue)
Expand All @@ -65,6 +67,7 @@ public static bool UseSystem {
}

static bool? is_vsts; // if the system-installed XI/XM should be used instead of the local one.

public static bool IsVsts {
get {
if (!is_vsts.HasValue)
Expand Down Expand Up @@ -147,14 +150,18 @@ static void ParseConfigFiles ()
tests_dir = file;
break;
}

dir = Path.GetDirectoryName (dir);
}

if (tests_dir is null)
throw new Exception ($"Could not find the directory 'tests'. Please run 'make' in the tests/ directory.");
throw new Exception (
$"Could not find the directory 'tests'. Please run 'make' in the tests/ directory.");
// Run make
ExecutionHelper.Execute ("make", new string [] { "-C", tests_dir, "test.config" });
test_config = FindConfigFiles ("test.config");
}

if (test_config.Any ())
ParseConfigFiles (test_config);
ParseConfigFiles (FindConfigFiles ("configure.inc"));
Expand Down Expand Up @@ -207,10 +214,14 @@ public static string EvaluateVariable (string variable)
return result;

var output = new StringBuilder ();
var rv = ExecutionHelper.Execute ("/usr/bin/make", new string [] { "-C", Path.Combine (SourceRoot, "tools", "devops"), "print-abspath-variable", $"VARIABLE={variable}" }, environmentVariables: null, stdout: output, stderr: output, timeout: TimeSpan.FromSeconds (5));
var rv = ExecutionHelper.Execute ("/usr/bin/make",
new string [] {
"-C", Path.Combine (SourceRoot, "tools", "devops"), "print-abspath-variable", $"VARIABLE={variable}"
}, environmentVariables: null, stdout: output, stderr: output, timeout: TimeSpan.FromSeconds (5));
if (rv != 0)
throw new Exception ($"Failed to evaluate variable '{variable}'. Exit code: {rv}. Output:\n{output}");
result = output.ToString ().Split (new char [] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Where (v => v.StartsWith (variable + "=", StringComparison.Ordinal)).SingleOrDefault ();
result = output.ToString ().Split (new char [] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
.Where (v => v.StartsWith (variable + "=", StringComparison.Ordinal)).SingleOrDefault ();
if (result is null)
throw new Exception ($"Could not find the variable '{variable}' to evaluate.");
return result.Substring (variable.Length + 1);
Expand All @@ -233,7 +244,8 @@ public static string GetPListStringValue (string plist, string key)
using (var fs = new StringReader (ReadPListAsXml (plist))) {
using (var reader = System.Xml.XmlReader.Create (fs, settings)) {
doc.Load (reader);
return doc.DocumentElement.SelectSingleNode ($"//dict/key[text()='{key}']/following-sibling::string[1]/text()").Value;
return doc.DocumentElement
.SelectSingleNode ($"//dict/key[text()='{key}']/following-sibling::string[1]/text()").Value;
}
}
}
Expand Down Expand Up @@ -278,7 +290,8 @@ static Configuration ()
DotNetExecutable = GetVariable ("DOTNET", null);
DotNetTfm = GetVariable ("DOTNET_TFM", null);
EnableXamarin = !string.IsNullOrEmpty (GetVariable ("ENABLE_XAMARIN", ""));
XcodeIsStable = string.Equals (GetVariable ("XCODE_IS_STABLE", ""), "true", StringComparison.OrdinalIgnoreCase);
XcodeIsStable = string.Equals (GetVariable ("XCODE_IS_STABLE", ""), "true",
StringComparison.OrdinalIgnoreCase);
DOTNET_DIR = GetVariable ("DOTNET_DIR", "");

XcodeVersionString = GetVariable ("XCODE_VERSION", GetXcodeVersion (xcode_root));
Expand Down Expand Up @@ -316,19 +329,23 @@ public static string RootPath {
throw new FormatException (".git worktree file is not valid");
}
}

if (Directory.Exists (path))
found = true;

if (!found) {
dir = Path.GetDirectoryName (dir);
if (dir is null)
throw new Exception ($"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
throw new Exception (
$"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
path = Path.Combine (dir, ".git");
}
}

path = Path.GetDirectoryName (path);
if (!Directory.Exists (path))
throw new Exception ($"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
throw new Exception (
$"Could not find the xamarin-macios repo given the test assembly directory {TestAssemblyDirectory}");
return path;
}
}
Expand Down Expand Up @@ -419,9 +436,11 @@ public static string GetNuGetVersionNoMetadata (TargetFramework framework)

public static string GetNuGetVersionNoMetadata (ApplePlatform platform)
{
var workloadVersion = Environment.GetEnvironmentVariable ($"{platform.AsString ().ToUpper ()}_WORKLOAD_VERSION");
return string.IsNullOrEmpty (workloadVersion) ?
GetVariable ($"{platform.AsString ().ToUpper ()}_NUGET_VERSION_NO_METADATA", string.Empty) : workloadVersion;
var workloadVersion =
Environment.GetEnvironmentVariable ($"{platform.AsString ().ToUpper ()}_WORKLOAD_VERSION");
return string.IsNullOrEmpty (workloadVersion)
? GetVariable ($"{platform.AsString ().ToUpper ()}_NUGET_VERSION_NO_METADATA", string.Empty)
: workloadVersion;
}

// This is only applicable for .NET
Expand Down Expand Up @@ -521,8 +540,8 @@ public static IList<string> GetArchitectures (string runtimeIdentifier)
public static IEnumerable<string> GetBaseLibraryImplementations ()
{
foreach (var platform in GetIncludedPlatforms ())
foreach (var lib in GetBaseLibraryImplementations (platform))
yield return lib;
foreach (var lib in GetBaseLibraryImplementations (platform))
yield return lib;
}

public static IEnumerable<string> GetBaseLibraryImplementations (ApplePlatform platform)
Expand Down Expand Up @@ -580,11 +599,14 @@ public static string CloneTestDirectory (string directory)
var testsTemporaryDirectory = Cache.CreateTemporaryDirectory ($"{Path.GetFileName (directory)}");

// Only copy files in git, we want a clean copy
var rv = ExecutionHelper.Execute ("git", new string [] { "ls-files" }, out var ls_files_output, working_directory: directory, timeout: TimeSpan.FromSeconds (15));
var rv = ExecutionHelper.Execute ("git", new string [] { "ls-files" }, out var ls_files_output,
working_directory: directory, timeout: TimeSpan.FromSeconds (15));
if (rv != 0)
throw new Exception ($"Failed to list test files. 'git ls-files' in {directory} failed with exit code {rv}.");
throw new Exception (
$"Failed to list test files. 'git ls-files' in {directory} failed with exit code {rv}.");

var files = ls_files_output.ToString ().Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries).ToArray ();
var files = ls_files_output.ToString ().Split (new char [] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.ToArray ();
foreach (var file in files) {
var src = Path.Combine (directory, file);
var tgt = Path.Combine (testsTemporaryDirectory, file);
Expand All @@ -593,7 +615,8 @@ public static string CloneTestDirectory (string directory)
File.Copy (src, tgt);
if (tgt.EndsWith (".csproj", StringComparison.OrdinalIgnoreCase)) {
var initialContents = File.ReadAllText (tgt);
var fixedContents = initialContents.Replace ($"$(MSBuildThisFileDirectory)", Path.GetDirectoryName (src) + Path.DirectorySeparatorChar);
var fixedContents = initialContents.Replace ($"$(MSBuildThisFileDirectory)",
Path.GetDirectoryName (src) + Path.DirectorySeparatorChar);
if (initialContents != fixedContents)
File.WriteAllText (tgt, fixedContents);
}
Expand Down Expand Up @@ -689,16 +712,19 @@ public static void Touch (string file)
}

static bool? is_apfs;

static bool IsAPFS {
get {
if (!is_apfs.HasValue) {
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
is_apfs = false;
} else {
var exit_code = ExecutionHelper.Execute ("/bin/df", new string [] { "-t", "apfs", "/" }, out var output, TimeSpan.FromSeconds (10));
var exit_code = ExecutionHelper.Execute ("/bin/df", new string [] { "-t", "apfs", "/" },
out var output, TimeSpan.FromSeconds (10));
is_apfs = exit_code == 0 && output.Trim ().Split ('\n').Length >= 2;
}
}

return is_apfs.Value;
}
}
Expand All @@ -714,17 +740,19 @@ static void EnsureFilestampChange ()

// Return true if the current machine can run ARM64 binaries.
static bool? canRunArm64;

public static bool CanRunArm64 {
get {
if (!canRunArm64.HasValue) {
int rv = 0;
IntPtr size = (IntPtr) sizeof (int);
IntPtr size = (IntPtr) sizeof(int);
if (sysctlbyname ("hw.optional.arm64", ref rv, ref size, IntPtr.Zero, IntPtr.Zero) == 0) {
canRunArm64 = rv == 1;
} else {
canRunArm64 = false;
}
}

return canRunArm64.Value;
}
}
Expand All @@ -739,6 +767,7 @@ public static IEnumerable<string> CallNM (string file, string nmArguments, strin
arguments.Add ("-arch");
arguments.Add (arch);
}

var symbols = ExecutionHelper.Execute ("nm", arguments, hide_output: true).Split ('\n');
return symbols.Where ((v) => {
return !v.EndsWith (": no symbols", StringComparison.Ordinal);
Expand All @@ -759,6 +788,22 @@ public static IEnumerable<string> GetUndefinedNativeSymbols (string file, string
{
return CallNM (file, "-gujA", arch);
}

public static bool TryGetApiDefinitionRsp (TargetFramework framework,
[NotNullWhen (true)] out string rspPath)
{
rspPath = null;
var platform = framework.Platform switch {
ApplePlatform.iOS => "ios",
ApplePlatform.TVOS => "tvos",
ApplePlatform.MacOSX => "macos",
ApplePlatform.MacCatalyst => "maccatalyst",
_ => null,
};
if (platform is null)
return false;
rspPath = Path.Combine (SourceRoot, "src", "build", "dotnet", platform, $"apidefinition-{platform}.rsp");
return true;
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void TryeCreateTests (ApplePlatform platform, (string Source, string Path) sourc
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void TryCreateTests (ApplePlatform platform, (string Source, string Path) source
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void TryCreateTests (ApplePlatform platform, (string Source, string Path) source
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Macios.Transformer.Extensions;
using Xamarin.Tests;
using Xamarin.Utils;

Expand All @@ -26,38 +27,38 @@ public class BaseTransformerTestClass {

protected Compilation CreateCompilation (ApplePlatform platform, [CallerMemberName] string name = "", params (string Source, string Path) [] sources)
{
// get the dotnet bcl and fully load it for the test.
var references = Directory.GetFiles (Configuration.DotNetBclDir, "*.dll")
.Select (assembly => MetadataReference.CreateFromFile (assembly)).ToList ();

// get the dll for the current platform, this is needed because that way we will get the attributes that
// are used in the old dlls that are needed to test the transformer.
var targetFramework = TargetFramework.GetTargetFramework (platform, isDotNet: true);
var platformDll = Configuration.GetBaseLibrary (targetFramework);
if (!string.IsNullOrEmpty (platformDll)) {
references.Add (MetadataReference.CreateFromFile (platformDll));
} else {
throw new InvalidOperationException ($"Could not find platform dll for {platform}");
}
// include the bgen attributes to the compilation, otherwise the transformer will not work.
var sourcesList = sources.ToList ();
if (Configuration.TryGetRootPath (out var rootPath)) {
var oldVersionAttrs = Path.Combine (rootPath, "src", "ObjCRuntime", "PlatformAvailability.cs");
sourcesList.Add ((File.ReadAllText (oldVersionAttrs), oldVersionAttrs));

var oldBgenAttrs = Path.Combine (rootPath, "src", "bgen", "Attributes.cs");
sourcesList.Add ((File.ReadAllText (oldBgenAttrs), oldBgenAttrs));
var workingDirectory = Path.Combine(Configuration.SourceRoot, "src");
if (!Configuration.TryGetApiDefinitionRsp (targetFramework, out var rspFile)) {
Assert.Fail ($"Could not find rsp file for {targetFramework}");
}

var parseResult = CSharpCommandLineParser.Default.ParseRsp (
rspFile, workingDirectory, Configuration.DotNetBclDir);

var parseOptions = new CSharpParseOptions (LanguageVersion.Latest, DocumentationMode.None, preprocessorSymbols: ["COREBUILD"]);
var trees = sourcesList.Select (
s => CSharpSyntaxTree.ParseText (s.Source, parseOptions, s.Path))
.ToImmutableArray ();
// add NET to the preprocessor directives
var preprocessorDirectives = parseResult.ParseOptions.PreprocessorSymbolNames.ToList ();
preprocessorDirectives.Add ("NET");

var options = new CSharpCompilationOptions (OutputKind.NetModule)
.WithAllowUnsafe (true);
// fixing the parsing options, we must have an issue in the rsp
var updatedParseOptions = parseResult.ParseOptions
.WithLanguageVersion (LanguageVersion.Latest)
.WithPreprocessorSymbols (preprocessorDirectives)
.WithDocumentationMode (DocumentationMode.None);

var references = parseResult.GetReferences (workingDirectory, Configuration.DotNetBclDir);
var parsedSource = parseResult.GetSourceFiles (updatedParseOptions).ToList ();
foreach (var (source, path) in sources) {
parsedSource.Add (CSharpSyntaxTree.ParseText (source, updatedParseOptions, path));
}

return CSharpCompilation.Create (name, trees, references, options);
return CSharpCompilation.Create (
assemblyName: name,
syntaxTrees: parsedSource,
references: references,
options: parseResult.CompilationOptions);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.CodeAnalysis;

namespace Microsoft.Macios.Transformer.Tests;

public static class SyntaxTreeExtensions {

public static SyntaxTree? ForSource (this IEnumerable<SyntaxTree> trees, (string Source, string Path) source)
=> trees.FirstOrDefault (t => t.FilePath == source.Path);
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void SkipTests (ApplePlatform platform, (string Source, string Path) sour
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down Expand Up @@ -240,7 +240,7 @@ public void SelectTopicTests (ApplePlatform platform, (string Source, string Pat
{
// create a compilation used to create the transformer
var compilation = CreateCompilation (platform, sources: source);
var syntaxTree = compilation.SyntaxTrees.FirstOrDefault ();
var syntaxTree = compilation.SyntaxTrees.ForSource (source);
Assert.NotNull (syntaxTree);

var semanticModel = compilation.GetSemanticModel (syntaxTree);
Expand Down

0 comments on commit d6eaa99

Please sign in to comment.