Skip to content

Commit f57fd6a

Browse files
committed
Add binding-integration-Tests (ongoing work).
context: dotnet#341 (comment) So far, it runs javac and jar to create input jars.
1 parent fed33a2 commit f57fd6a

File tree

6 files changed

+350
-1
lines changed

6 files changed

+350
-1
lines changed

Java.Interop.sln

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{4C173212-3
1717
EndProject
1818
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop", "src\Java.Interop\Java.Interop.csproj", "{94BD81F7-B06F-4295-9636-F8A3B6BDC762}"
1919
EndProject
20-
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "java-interop", "src\java-interop\java-interop.csproj", "{BB0AB9F7-0979-41A7-B7A9-877260655F94}"
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "java-interop", "src\java-interop\java-interop.csproj", "{BB0AB9F7-0979-41A7-B7A9-877260655F94}"
2121
EndProject
2222
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.Dynamic", "src\Java.Interop.Dynamic\Java.Interop.Dynamic.csproj", "{AD4468F8-8883-434B-9D4C-E1801BB3B52A}"
2323
EndProject
@@ -99,6 +99,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.GenericMarshal
9999
EndProject
100100
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java.Interop.BootstrapTasks", "src\Java.Interop.BootstrapTasks\Java.Interop.BootstrapTasks.csproj", "{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}"
101101
EndProject
102+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "binding-integration-Tests", "tests\binding-integrated-Tests\binding-integration-Tests.csproj", "{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}"
103+
EndProject
102104
Global
103105
GlobalSection(SolutionConfigurationPlatforms) = preSolution
104106
Debug|Any CPU = Debug|Any CPU
@@ -349,6 +351,14 @@ Global
349351
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
350352
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU
351353
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU
354+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
355+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Debug|Any CPU.Build.0 = Debug|Any CPU
356+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Release|Any CPU.ActiveCfg = Release|Any CPU
357+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.Release|Any CPU.Build.0 = Release|Any CPU
358+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationDebug|Any CPU.ActiveCfg = Debug|Any CPU
359+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationDebug|Any CPU.Build.0 = Debug|Any CPU
360+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationRelease|Any CPU.ActiveCfg = Release|Any CPU
361+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E}.XAIntegrationRelease|Any CPU.Build.0 = Release|Any CPU
352362
EndGlobalSection
353363
GlobalSection(NestedProjects) = preSolution
354364
{0C001D50-4176-45AE-BDC8-BA626508B0CC} = {C8F58966-94BF-407F-914A-8654F8B8AE3B}
@@ -392,5 +402,6 @@ Global
392402
{C0487169-8F81-497F-919E-EB42B1D0243F} = {C8F58966-94BF-407F-914A-8654F8B8AE3B}
393403
{D1243BAB-23CA-4566-A2A3-3ADA2C2DC3AF} = {4C173212-371D-45D8-BA83-9226194F48DC}
394404
{3E8E5C8C-59A6-4A9A-B55D-46AB14431B2A} = {172B608B-E6F3-41CC-9949-203A76BA247C}
405+
{D5CE4B09-C1D3-4647-B78B-6D2E89FE883E} = {271C9F30-F679-4793-942B-0D9527CB3E2F}
395406
EndGlobalSection
396407
EndGlobal
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using NUnit.Framework;
2+
using System;
3+
using System.Diagnostics;
4+
using Java.Interop;
5+
using System.IO;
6+
using System.Linq;
7+
8+
namespace BindingIntegrationTests
9+
{
10+
public class BindingBuilder
11+
{
12+
[Flags]
13+
public enum Steps
14+
{
15+
Javac,
16+
Jar,
17+
ClassParse,
18+
ApiXmlAdjuster,
19+
Generator,
20+
Csc,
21+
All = Javac | Jar | ClassParse | ApiXmlAdjuster | Generator | Csc
22+
}
23+
24+
public const string JavaSourcesSubDir = "java-sources";
25+
public const string ClassesSubDir = "classes";
26+
27+
public Steps ProcessSteps { get; set; } = Steps.All;
28+
29+
// entire work (intermediate output) directory
30+
public string IntermediateOutputPathRelative { get; set; } = "intermediate-output";
31+
32+
// Used to resolve javac and rt.jar
33+
public string JdkPath { get; set; }
34+
35+
static string ProbeJavaHome ()
36+
{
37+
var env = Environment.GetEnvironmentVariable ("JAVA_HOME");
38+
if (!string.IsNullOrEmpty (env))
39+
return env;
40+
return "/usr/lib/jvm/java-8-openjdk-amd64/";
41+
}
42+
43+
public static BindingBuilder CreateBestBetDefault (BindingProject project)
44+
{
45+
return new BindingBuilder (project) { JdkPath = ProbeJavaHome () };
46+
}
47+
48+
public BindingBuilder (BindingProject project)
49+
{
50+
this.project = project;
51+
}
52+
53+
readonly BindingProject project;
54+
55+
public string IntermediateOutputPathAbsolute => Path.Combine (Path.GetDirectoryName (new Uri (GetType ().Assembly.CodeBase).LocalPath), IntermediateOutputPathRelative, project.Id);
56+
57+
public void Clean ()
58+
{
59+
if (Directory.Exists (IntermediateOutputPathAbsolute))
60+
Directory.Delete (IntermediateOutputPathAbsolute, true);
61+
}
62+
63+
public void Build ()
64+
{
65+
Javac ();
66+
Jar ();
67+
ClassParse ();
68+
AdjustApiXml ();
69+
GenerateBindingSources ();
70+
CompileBindings ();
71+
}
72+
73+
Action<string> ensureDirectory = dir => { if (!Directory.Exists (dir)) Directory.CreateDirectory (dir); };
74+
75+
void Javac ()
76+
{
77+
if ((ProcessSteps & Steps.Javac) != Steps.Javac)
78+
return;
79+
80+
if (JdkPath == null)
81+
throw new InvalidOperationException ("JdkPath is not set.");
82+
83+
var objDir = IntermediateOutputPathAbsolute;
84+
ensureDirectory (objDir);
85+
86+
string sourcesSaved = Path.Combine (objDir, JavaSourcesSubDir);
87+
ensureDirectory (sourcesSaved);
88+
foreach (var item in project.JavaSourceStrings)
89+
File.WriteAllText (Path.Combine (sourcesSaved, item.FileName), item.Content);
90+
var sourceFiles = project.JavaSourceFiles.Concat (project.JavaSourceStrings.Select (i => Path.Combine (sourcesSaved, i.FileName)));
91+
92+
if (project.CompiledClassesDirectory == null)
93+
project.CompiledClassesDirectory = Path.Combine (objDir, ClassesSubDir);
94+
ensureDirectory (project.CompiledClassesDirectory);
95+
96+
var psi = new ProcessStartInfo () {
97+
UseShellExecute = false,
98+
FileName = JdkPath != null ? Path.Combine (JdkPath, "bin", "javac") : "javac",
99+
Arguments = $"{project.JavacOptions} -d \"{project.CompiledClassesDirectory}\" {string.Join (" ", sourceFiles.Select (s => '"' + s + '"'))}",
100+
RedirectStandardOutput = true,
101+
RedirectStandardError = true,
102+
};
103+
if (project.CustomRuntimeJar != null)
104+
psi.Arguments += $" -bootclasspath {project.CustomRuntimeJar} -classpath {project.CustomRuntimeJar}";
105+
106+
project.JavacExecutionOutput = $"Execute javac as: {psi.FileName} {psi.Arguments}\n";
107+
108+
var proc = new Process () { StartInfo = psi };
109+
proc.OutputDataReceived += (sender, e) => project.JavacExecutionOutput += e.Data;
110+
proc.ErrorDataReceived += (sender, e) => project.JavacExecutionOutput += e.Data;
111+
proc.Start ();
112+
proc.BeginOutputReadLine ();
113+
proc.BeginErrorReadLine ();
114+
proc.WaitForExit ();
115+
if (proc.ExitCode != 0)
116+
throw new Exception ("Javac failed: " + project.JavacExecutionOutput);
117+
}
118+
119+
void Jar ()
120+
{
121+
if ((ProcessSteps & Steps.Jar) != Steps.Jar)
122+
return;
123+
124+
if (JdkPath == null)
125+
throw new InvalidOperationException ("JdkPath is not set.");
126+
127+
var objDir = IntermediateOutputPathAbsolute;
128+
if (project.CompiledClassesDirectory == null)
129+
project.CompiledClassesDirectory = Path.Combine (objDir, ClassesSubDir);
130+
project.CompiledJarFile = Path.Combine (project.CompiledClassesDirectory, project.Id + ".jar");
131+
132+
var psi = new ProcessStartInfo () {
133+
UseShellExecute = false,
134+
FileName = JdkPath != null ? Path.Combine (JdkPath, "bin", "jar") : "jar",
135+
Arguments = $"cvf \"{project.CompiledJarFile}\" -C \"{project.CompiledClassesDirectory}\" .",
136+
RedirectStandardOutput = true,
137+
RedirectStandardError = true,
138+
};
139+
140+
project.JarExecutionOutput = $"Execute jar as: {psi.FileName} {psi.Arguments}\n";
141+
142+
var proc = new Process () { StartInfo = psi };
143+
proc.OutputDataReceived += (sender, e) => project.JarExecutionOutput += e.Data;
144+
proc.ErrorDataReceived += (sender, e) => project.JarExecutionOutput += e.Data;
145+
proc.Start ();
146+
proc.BeginOutputReadLine ();
147+
proc.BeginErrorReadLine ();
148+
proc.WaitForExit ();
149+
if (proc.ExitCode != 0)
150+
throw new Exception ("Jar failed: " + project.JarExecutionOutput);
151+
}
152+
153+
void ClassParse ()
154+
{
155+
}
156+
157+
void AdjustApiXml ()
158+
{
159+
}
160+
161+
void GenerateBindingSources ()
162+
{
163+
}
164+
165+
void CompileBindings ()
166+
{
167+
}
168+
}
169+
}
170+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Collections.Generic;
2+
3+
namespace BindingIntegrationTests
4+
{
5+
public class JavaSource
6+
{
7+
public string FileName { get; set; }
8+
public string Content { get; set; }
9+
}
10+
11+
public class BindingProject
12+
{
13+
public string Id { get; set; }
14+
15+
// initial inputs
16+
public IList<string> JavaSourceFiles { get; set; } = new List<string> ();
17+
public IList<JavaSource> JavaSourceStrings { get; set; } = new List<JavaSource> ();
18+
public string JavacOptions { get; set; } = "-g";
19+
// rt.jar, android.jar etc.
20+
// If it is specified, then javac will run with -bootclasspath and -cp
21+
public string CustomRuntimeJar { get; set; }
22+
23+
public IList<string> ApiXmlFiles { get; set; } = new List<string> ();
24+
public IList<string> MetadataXmlFiles { get; set; } = new List<string> ();
25+
//public IList<string> MetadataXmlStrings { get; set; }
26+
public IList<string> InputJarFiles { get; set; } = new List<string> ();
27+
public IList<string> ReferenceJarFiles { get; set; } = new List<string> ();
28+
public IList<string> CSharpSourceFiles { get; set; } = new List<string> ();
29+
//public IList<string> CSharpSourceStrings { get; set; } = new List<string> ();
30+
public IList<string> ReferenceDlls { get; set; } = new List<string> ();
31+
32+
public string JavacExecutionOutput { get; internal set; }
33+
// Java classes directory generated by javac (Javac() method)
34+
public string CompiledClassesDirectory { get; set; }
35+
36+
public string JarExecutionOutput { get; internal set; }
37+
// Java libraries archived by jar
38+
public string CompiledJarFile { get; internal set; }
39+
40+
// API XML description generated by class-parse
41+
public string GeneratedClassParseXmlFile { get; internal set; }
42+
// API XML description generated by api-xml-adjuster
43+
public string GeneratedApiXmlFile { get; internal set; }
44+
45+
// C# sources generated by generator
46+
public IList<string> GeneratedCSharpSourceFiles { get; internal set; }
47+
// DLL built by C# compiler
48+
public string GeneratedDllFile { get; internal set; }
49+
}
50+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
This directory contains a set of integration tests for binding infrastructure.
2+
3+
The test fixtures make it possible to build binding Java library, export
4+
API, generate binding sources, compile them and even execute it on JVM.
5+
6+
### Input items
7+
8+
- Java sources
9+
- Metadata fixup
10+
- input Java libraries
11+
- reference Java libraries
12+
- reference managed libraries
13+
14+
### Tools
15+
16+
- class-parse
17+
- api-xml-adjuster
18+
- generator
19+
- csc
20+
21+
### Build artifacts to test
22+
23+
- jars
24+
- dlls
25+
- generated C# sources
26+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.IO;
3+
using NUnit.Framework;
4+
5+
namespace BindingIntegrationTests
6+
{
7+
// Whenever we write complicated test foundation, we should ensure that it actually works.
8+
public class SampleTest
9+
{
10+
[Test]
11+
public void YouNeedJdkPath ()
12+
{
13+
Assert.Throws<InvalidOperationException> (() => {
14+
new BindingBuilder (new BindingProject ()).Build ();
15+
});
16+
}
17+
18+
[Test]
19+
public void VerifyJavac ()
20+
{
21+
// You don't even have to create a set of project files. They can be created on the fly.
22+
var project = new BindingProject { Id = nameof (VerifyJavac) };
23+
string fooJavaFileName = "Foo.java";
24+
string fooJavaContent = "public class Foo {}";
25+
project.JavaSourceStrings.Add (new JavaSource { FileName = fooJavaFileName, Content = fooJavaContent });
26+
27+
// Set up builder. Hopefully you don't have to provide JDK path, it will be probed.
28+
var builder = BindingBuilder.CreateBestBetDefault (project);
29+
builder.ProcessSteps = BindingBuilder.Steps.Javac;
30+
builder.Clean ();
31+
builder.Build ();
32+
33+
var savedFooJavaFile = Path.Combine (builder.IntermediateOutputPathAbsolute, BindingBuilder.JavaSourcesSubDir, fooJavaFileName);
34+
Assert.IsTrue (File.Exists (savedFooJavaFile), "Java source not saved");
35+
Assert.AreEqual (fooJavaContent, File.ReadAllText (savedFooJavaFile), "Saved java content mismatch.");
36+
var classesDir = Path.Combine (builder.IntermediateOutputPathAbsolute, BindingBuilder.ClassesSubDir);
37+
Assert.AreEqual (classesDir, project.CompiledClassesDirectory, "classes directory mismatch.");
38+
var fooClassFile = Path.Combine (classesDir, "Foo.class");
39+
Assert.IsTrue (File.Exists (fooClassFile), "Compiled Foo.class not found");
40+
}
41+
42+
[Test]
43+
public void VerifyJavacWithRtJar ()
44+
{
45+
var project = new BindingProject { Id = nameof (VerifyJavacWithRtJar) };
46+
string fooJavaFileName = "Foo.java";
47+
string fooJavaContent = "public class Foo {}";
48+
project.JavaSourceStrings.Add (new JavaSource { FileName = fooJavaFileName, Content = fooJavaContent });
49+
50+
var builder = BindingBuilder.CreateBestBetDefault (project);
51+
project.CustomRuntimeJar = Path.Combine (builder.JdkPath, "jre", "lib", "rt.jar");
52+
Assert.IsTrue (File.Exists (project.CustomRuntimeJar), "rt.jar exists");
53+
builder.ProcessSteps = BindingBuilder.Steps.Javac;
54+
builder.Clean ();
55+
builder.Build ();
56+
}
57+
58+
[Test]
59+
public void VerifyJar ()
60+
{
61+
// You don't even have to create a set of project files. They can be created on the fly.
62+
var project = new BindingProject { Id = nameof (VerifyJar) };
63+
project.JavaSourceStrings.Add (new JavaSource { FileName = "Foo.java", Content = "public class Foo {}" });
64+
project.JavaSourceStrings.Add (new JavaSource { FileName = "Bar.java", Content = "public class Bar {}" });
65+
66+
// Set up builder. Hopefully you don't have to provide JDK path, it will be probed.
67+
var builder = BindingBuilder.CreateBestBetDefault (project);
68+
builder.ProcessSteps = BindingBuilder.Steps.Javac | BindingBuilder.Steps.Jar;
69+
builder.Clean ();
70+
builder.Build ();
71+
72+
var jar = Path.Combine (builder.IntermediateOutputPathAbsolute, BindingBuilder.ClassesSubDir, project.Id + ".jar");
73+
Assert.AreEqual (jar, project.CompiledJarFile, "jar file path mismatch.");
74+
Assert.IsTrue (File.Exists (project.CompiledJarFile), "Compiled jar not found");
75+
}
76+
}
77+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net461</TargetFramework>
4+
<RootNamespace>BindingIntegrationTests</RootNamespace>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<PackageReference Include="NUnit" Version="3.10.1" />
8+
</ItemGroup>
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\src\Java.Interop\Java.Interop.csproj" />
11+
<ProjectReference Include="..\TestJVM\TestJVM.csproj" />
12+
<ProjectReference Include="..\..\src\Xamarin.Android.Tools.Bytecode\Xamarin.Android.Tools.Bytecode.csproj" />
13+
<ProjectReference Include="..\..\src\Xamarin.Android.Tools.ApiXmlAdjuster\Xamarin.Android.Tools.ApiXmlAdjuster.csproj" />
14+
</ItemGroup>
15+
</Project>

0 commit comments

Comments
 (0)