Skip to content

Commit b259e62

Browse files
WIP
1 parent 0e4e824 commit b259e62

File tree

3 files changed

+148
-79
lines changed

3 files changed

+148
-79
lines changed

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs

Lines changed: 92 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ public void FixAbstractMethodsStep_SkipDimMembers ()
2525
context.Resolver.AddSearchDirectory (path);
2626

2727
var myAssemblyPath = Path.Combine (path, "MyAssembly.dll");
28-
29-
using (var android = CreateFauxMonoAndroidAssembly ()) {
30-
android.Write (Path.Combine (path, "Mono.Android.dll"));
31-
CreateAbstractIfaceImplementation (myAssemblyPath, android);
32-
}
28+
var monoAndroidPath = Path.Combine (path, "Mono.Android.dll");
29+
CreateFauxMonoAndroidAssembly (monoAndroidPath);
30+
CreateAbstractIfaceImplementation (myAssemblyPath, monoAndroidPath);
3331

3432
using (var assm = context.Resolve (myAssemblyPath)) {
3533
step.Process (context);
@@ -44,116 +42,131 @@ public void FixAbstractMethodsStep_SkipDimMembers ()
4442
Directory.Delete (path, true);
4543
}
4644

47-
static void CreateAbstractIfaceImplementation (string assemblyPath, AssemblyDefinition android)
45+
static void CreateAbstractIfaceImplementation (string assemblyPath, string monoAndroidPath)
4846
{
49-
using (var assm = AssemblyDefinition.CreateAssembly (new AssemblyNameDefinition ("DimTest", new Version ()), "DimTest", ModuleKind.Dll)) {
50-
var void_type = assm.MainModule.ImportReference (typeof (void));
47+
//using (var assm = AssemblyDefinition.CreateAssembly (new AssemblyNameDefinition ("DimTest", new Version ()), "DimTest", ModuleKind.Dll)) {
48+
// var void_type = assm.MainModule.ImportReference (typeof (void));
5149

52-
// Create interface
53-
var iface = new TypeDefinition ("MyNamespace", "IMyInterface", TypeAttributes.Interface);
50+
// // Create interface
51+
// var iface = new TypeDefinition ("MyNamespace", "IMyInterface", TypeAttributes.Interface);
5452

55-
var abstract_method = new MethodDefinition ("MyAbstractMethod", MethodAttributes.Abstract, void_type);
56-
var default_method = new MethodDefinition ("MyDefaultMethod", MethodAttributes.Public, void_type);
53+
// var abstract_method = new MethodDefinition ("MyAbstractMethod", MethodAttributes.Abstract, void_type);
54+
// var default_method = new MethodDefinition ("MyDefaultMethod", MethodAttributes.Public, void_type);
5755

58-
iface.Methods.Add (abstract_method);
59-
iface.Methods.Add (default_method);
56+
// iface.Methods.Add (abstract_method);
57+
// iface.Methods.Add (default_method);
6058

61-
assm.MainModule.Types.Add (iface);
59+
// assm.MainModule.Types.Add (iface);
6260

63-
// Create implementing class
64-
var jlo = assm.MainModule.Import (android.MainModule.GetType ("Java.Lang.Object"));
65-
var impl = new TypeDefinition ("MyNamespace", "MyClass", TypeAttributes.Public, jlo);
66-
impl.Interfaces.Add (new InterfaceImplementation (iface));
61+
// // Create implementing class
62+
// var jlo = assm.MainModule.Import (android.MainModule.GetType ("Java.Lang.Object"));
63+
// var impl = new TypeDefinition ("MyNamespace", "MyClass", TypeAttributes.Public, jlo);
64+
// impl.Interfaces.Add (new InterfaceImplementation (iface));
6765

68-
assm.MainModule.Types.Add (impl);
69-
assm.Write (assemblyPath);
70-
}
66+
// assm.MainModule.Types.Add (impl);
67+
// assm.Write (assemblyPath);
68+
//}
7169
}
7270

7371
[Test]
74-
public void FixAbstractMethodsStep_NestedTypes ()
72+
public void FixAbstractMethodsStep_Explicit ()
7573
{
7674
var path = Path.Combine (Path.GetFullPath (XABuildPaths.TestOutputDirectory), "temp", TestName);
7775
var step = new FixAbstractMethodsStep ();
7876
var pipeline = new Pipeline ();
7977

8078
Directory.CreateDirectory (path);
8179

82-
using (var context = new LinkContext (pipeline)) {
80+
var myAssemblyPath = Path.Combine (path, "MyAssembly.dll");
81+
var monoAndroidPath = Path.Combine (path, "Mono.Android.dll");
82+
CreateFauxMonoAndroidAssembly (monoAndroidPath);
83+
Compiler.Compile (@"
84+
namespace MyNamespace
85+
{
86+
public interface IMyInterface
87+
{
88+
void MyMethod ();
89+
}
90+
91+
public class MyClass : Java.Lang.Object, IMyInterface
92+
{
93+
void IMyInterface.MyMethod () { }
94+
}
95+
}
96+
", myAssemblyPath, monoAndroidPath);
8397

98+
using (var context = new LinkContext (pipeline)) {
8499
context.Resolver.AddSearchDirectory (path);
85100

86-
var myAssemblyPath = Path.Combine (path, "MyAssembly.dll");
87-
88-
using (var android = CreateFauxMonoAndroidAssembly ()) {
89-
android.Write (Path.Combine (path, "Mono.Android.dll"));
90-
CreateNestedInterface (myAssemblyPath, android);
91-
}
92-
93101
using (var assm = context.Resolve (myAssemblyPath)) {
94102
step.Process (context);
95103

96-
var impl = assm.MainModule.GetType ("MyNamespace.Nested/MyClass");
97-
var method = impl.Methods.FirstOrDefault (m => m.Name == "MyImplementedMethod");
98-
Assert.IsNotNull (method, "MyImplementedMethod should exist");
99-
Assert.AreEqual (0, method.Body.Instructions.Count, "MyImplementedMethod should be an empty method");
100-
method = impl.Methods.FirstOrDefault (m => m.Name == "MyMissingMethod");
101-
Assert.IsNotNull (method, "MyMissingMethod should exist");
102-
Assert.AreNotEqual (0, method.Body.Instructions.Count, "MyMissingMethod should *not* be an empty method");
104+
var impl = assm.MainModule.GetType ("MyNamespace.MyClass");
105+
var method = impl.Methods.FirstOrDefault (m => m.Name == "MyNamespace.IMyInterface.MyMethod");
106+
Assert.IsNotNull (method, "MyMethod should exist");
107+
Assert.AreEqual (1, method.Body.Instructions.Count, "MyMethod should be an empty method");
103108
}
104109
}
105110

106111
Directory.Delete (path, true);
107112
}
108113

109-
static void CreateNestedInterface (string assemblyPath, AssemblyDefinition android)
114+
[Test]
115+
public void FixAbstractMethodsStep_Virtual ()
110116
{
111-
using (var assm = AssemblyDefinition.CreateAssembly (new AssemblyNameDefinition ("NestedIFaceTest", new Version ()), "NestedIFaceTest", ModuleKind.Dll)) {
112-
var void_type = assm.MainModule.ImportReference (typeof (void));
113-
var int_type = assm.MainModule.ImportReference (typeof (int));
114-
115-
// Create nested interface
116-
var nested = new TypeDefinition ("MyNamespace", "Nested", TypeAttributes.Class);
117-
var iface = new TypeDefinition (null, "IMyInterface", TypeAttributes.Interface);
118-
iface.DeclaringType = nested;
119-
nested.NestedTypes.Add (iface);
120-
121-
var impl_method = new MethodDefinition ("MyImplementedMethod", MethodAttributes.Abstract, void_type);
122-
iface.Methods.Add (impl_method);
123-
var missing_method = new MethodDefinition ("MyMissingMethod", MethodAttributes.Abstract, void_type);
124-
iface.Methods.Add (missing_method);
125-
assm.MainModule.Types.Add (nested);
126-
assm.MainModule.Types.Add (iface);
127-
128-
// Create nested implementing class
129-
var jlo = assm.MainModule.ImportReference (android.MainModule.GetType ("Java.Lang.Object"));
130-
var impl = new TypeDefinition (null, "MyClass", TypeAttributes.Public, jlo);
131-
impl.Interfaces.Add (new InterfaceImplementation (iface));
132-
impl.DeclaringType = nested;
133-
nested.NestedTypes.Add (impl);
134-
135-
impl.Methods.Add (new MethodDefinition (impl_method.Name, MethodAttributes.Public, void_type));
136-
137-
assm.MainModule.Types.Add (impl);
138-
assm.Write (assemblyPath);
117+
var path = Path.Combine (Path.GetFullPath (XABuildPaths.TestOutputDirectory), "temp", TestName);
118+
var step = new FixAbstractMethodsStep ();
119+
var pipeline = new Pipeline ();
120+
121+
Directory.CreateDirectory (path);
122+
123+
var myAssemblyPath = Path.Combine (path, "MyAssembly.dll");
124+
var monoAndroidPath = Path.Combine (path, "Mono.Android.dll");
125+
CreateFauxMonoAndroidAssembly (monoAndroidPath);
126+
Compiler.Compile (@"
127+
namespace MyNamespace
128+
{
129+
public abstract class MyAbstract : Java.Lang.Object, Java.Lang.IJavaObject
130+
{
131+
public abstract void MyMethod ();
132+
}
133+
134+
public class MyClass : MyAbstract
135+
{
136+
public override void MyMethod () { }
137+
}
138+
}
139+
", myAssemblyPath, monoAndroidPath);
140+
141+
using (var context = new LinkContext (pipeline)) {
142+
143+
context.Resolver.AddSearchDirectory (path);
144+
145+
using (var assm = context.Resolve (myAssemblyPath)) {
146+
step.Process (context);
147+
148+
var impl = assm.MainModule.GetType ("MyNamespace.MyClass");
149+
var method = impl.Methods.FirstOrDefault (m => m.Name == "MyMethod");
150+
Assert.IsNotNull (method, "MyMethod should exist");
151+
Assert.AreEqual (1, method.Body.Instructions.Count, "MyMethod should be an empty method");
152+
}
139153
}
154+
155+
Directory.Delete (path, true);
140156
}
141157

142-
static AssemblyDefinition CreateFauxMonoAndroidAssembly ()
158+
static void CreateFauxMonoAndroidAssembly (string outputPath)
143159
{
144-
var assm = AssemblyDefinition.CreateAssembly (new AssemblyNameDefinition ("Mono.Android", new Version ()), "DimTest", ModuleKind.Dll);
145-
var void_type = assm.MainModule.ImportReference (typeof (void));
146-
147-
// Create fake JLO type
148-
var jlo = new TypeDefinition ("Java.Lang", "Object", TypeAttributes.Public);
149-
assm.MainModule.Types.Add (jlo);
160+
Compiler.Compile (@"
161+
namespace Java.Lang
162+
{
163+
public interface IJavaObject { }
150164
151-
// Create fake Java.Lang.AbstractMethodError type
152-
var ame = new TypeDefinition ("Java.Lang", "AbstractMethodError", TypeAttributes.Public);
153-
ame.Methods.Add (new MethodDefinition (".ctor", MethodAttributes.Public, void_type));
154-
assm.MainModule.Types.Add (ame);
165+
public class Object : IJavaObject { }
155166
156-
return assm;
167+
public class AbstractMethodError { }
168+
}
169+
", outputPath);
157170
}
158171

159172
[Test]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
extern alias system;
2+
using System;
3+
using System.IO;
4+
using System.Text;
5+
using NUnit.Framework;
6+
using system::System.CodeDom.Compiler;
7+
8+
namespace Xamarin.Android.Build.Tests
9+
{
10+
static class Compiler
11+
{
12+
const string RoslynEnvironmentVariable = "ROSLYN_COMPILER_LOCATION";
13+
static string unitTestFrameworkAssemblyPath = typeof (Assert).Assembly.Location;
14+
15+
static CodeDomProvider GetCodeDomProvider ()
16+
{
17+
if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
18+
//NOTE: there is an issue where Roslyn's csc.exe isn't copied to output for non-ASP.NET projects
19+
// Comments on this here: https://stackoverflow.com/a/40311406/132442
20+
// They added an environment variable as a workaround: https://github.com/aspnet/RoslynCodeDomProvider/pull/12
21+
if (string.IsNullOrEmpty (Environment.GetEnvironmentVariable (RoslynEnvironmentVariable, EnvironmentVariableTarget.Process))) {
22+
string roslynPath = Path.GetFullPath (Path.Combine (unitTestFrameworkAssemblyPath, "..", "..", "..", "packages", "microsoft.codedom.providers.dotnetcompilerplatform", "2.0.1", "tools", "RoslynLatest"));
23+
Environment.SetEnvironmentVariable (RoslynEnvironmentVariable, roslynPath, EnvironmentVariableTarget.Process);
24+
}
25+
26+
return new Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider ();
27+
} else {
28+
return new system::Microsoft.CSharp.CSharpCodeProvider ();
29+
}
30+
}
31+
32+
public static void Compile (string source, string outputPath, params string[] references)
33+
{
34+
var parameters = new CompilerParameters {
35+
GenerateExecutable = false,
36+
IncludeDebugInformation = false,
37+
OutputAssembly = outputPath,
38+
CompilerOptions = "/optimize+",
39+
};
40+
if (references != null)
41+
parameters.ReferencedAssemblies.AddRange (references);
42+
using (var codeProvider = GetCodeDomProvider ()) {
43+
var results = codeProvider.CompileAssemblyFromSource (parameters, source);
44+
if (results.Errors.Count > 0) {
45+
var builder = new StringBuilder ();
46+
foreach (CompilerError message in results.Errors) {
47+
builder.AppendLine (message.ToString ());
48+
}
49+
Assert.Fail (builder.ToString ());
50+
}
51+
}
52+
}
53+
}
54+
}

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Xamarin.Android.Build.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<Import Project="..\..\..\..\build-tools\scripts\MSBuildReferences.projitems" />
1212

1313
<ItemGroup>
14+
<Reference Include="System" Aliases="global,system" />
1415
<Reference Include="Xamarin.Android.Cecil">
1516
<HintPath>..\..\..\..\bin\$(Configuration)\lib\xamarin.android\xbuild\Xamarin\Android\Xamarin.Android.Cecil.dll</HintPath>
1617
</Reference>
@@ -20,6 +21,7 @@
2021
<PackageReference Include="NUnit" Version="3.11.0" />
2122
<PackageReference Include="NUnit.ConsoleRunner" Version="3.9.0" />
2223
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
24+
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="2.0.1" />
2325
</ItemGroup>
2426

2527
<ItemGroup>

0 commit comments

Comments
 (0)