Skip to content

Commit 55aa377

Browse files
committed
[TrimmableTypeMap] Foundational scanner unit tests slice
1 parent cf78bbe commit 55aa377

File tree

5 files changed

+1090
-0
lines changed

5 files changed

+1090
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(DotNetStableTargetFramework)</TargetFramework>
5+
<LangVersion>latest</LangVersion>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
<RootNamespace>Microsoft.Android.Sdk.TrimmableTypeMap.Tests</RootNamespace>
9+
</PropertyGroup>
10+
11+
<!-- Exclude TestFixtures subdirectory — it's a separate project compiled independently -->
12+
<ItemGroup>
13+
<Compile Remove="TestFixtures\**" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="xunit" Version="2.9.3" />
18+
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2" />
19+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Include="..\..\src\Microsoft.Android.Sdk.TrimmableTypeMap\Microsoft.Android.Sdk.TrimmableTypeMap.csproj" />
24+
<ProjectReference Include="TestFixtures\TestFixtures.csproj">
25+
<!-- Don't add as a compile-time reference — we only need the built DLL as scanner test input -->
26+
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
27+
</ProjectReference>
28+
</ItemGroup>
29+
30+
<!-- Copy TestFixtures output to our output directory so tests can find it -->
31+
<Target Name="_CopyTestFixturesOutput" AfterTargets="Build">
32+
<ItemGroup>
33+
<_TestFixtureFiles Include="TestFixtures\bin\$(Configuration)\$(DotNetStableTargetFramework)\TestFixtures.dll" />
34+
</ItemGroup>
35+
<Copy SourceFiles="@(_TestFixtureFiles)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" />
36+
</Target>
37+
38+
</Project>
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using Xunit;
7+
8+
namespace Microsoft.Android.Sdk.TrimmableTypeMap.Tests;
9+
10+
public partial class JavaPeerScannerTests
11+
{
12+
static string TestFixtureAssemblyPath {
13+
get {
14+
var testAssemblyDir = Path.GetDirectoryName (typeof (JavaPeerScannerTests).Assembly.Location)!;
15+
var fixtureAssembly = Path.Combine (testAssemblyDir, "TestFixtures.dll");
16+
Assert.True (File.Exists (fixtureAssembly),
17+
$"TestFixtures.dll not found at {fixtureAssembly}. Ensure the TestFixtures project builds.");
18+
return fixtureAssembly;
19+
}
20+
}
21+
22+
List<JavaPeerInfo> ScanFixtures ()
23+
{
24+
using var scanner = new JavaPeerScanner ();
25+
return scanner.Scan (new [] { TestFixtureAssemblyPath });
26+
}
27+
28+
JavaPeerInfo FindByJavaName (List<JavaPeerInfo> peers, string javaName)
29+
{
30+
var peer = peers.FirstOrDefault (p => p.JavaName == javaName);
31+
Assert.NotNull (peer);
32+
return peer;
33+
}
34+
35+
JavaPeerInfo FindByManagedName (List<JavaPeerInfo> peers, string managedName)
36+
{
37+
var peer = peers.FirstOrDefault (p => p.ManagedTypeName == managedName);
38+
Assert.NotNull (peer);
39+
return peer;
40+
}
41+
42+
[Fact]
43+
public void Scan_FindsAllJavaPeerTypes ()
44+
{
45+
var peers = ScanFixtures ();
46+
Assert.NotEmpty (peers);
47+
// MCW types with [Register]
48+
Assert.Contains (peers, p => p.JavaName == "java/lang/Object");
49+
Assert.Contains (peers, p => p.JavaName == "android/app/Activity");
50+
// User type with JNI name from [Activity(Name="...")]
51+
Assert.Contains (peers, p => p.JavaName == "my/app/MainActivity");
52+
// Exception/Throwable hierarchy
53+
Assert.Contains (peers, p => p.JavaName == "java/lang/Throwable");
54+
Assert.Contains (peers, p => p.JavaName == "java/lang/Exception");
55+
}
56+
57+
[Fact]
58+
public void Scan_McwTypes_HaveDoNotGenerateAcw ()
59+
{
60+
var peers = ScanFixtures ();
61+
62+
var activity = FindByJavaName (peers, "android/app/Activity");
63+
Assert.True (activity.DoNotGenerateAcw, "Activity should be MCW (DoNotGenerateAcw=true)");
64+
65+
var button = FindByJavaName (peers, "android/widget/Button");
66+
Assert.True (button.DoNotGenerateAcw, "Button should be MCW");
67+
}
68+
69+
[Fact]
70+
public void Scan_UserTypes_DoNotGenerateAcwIsFalse ()
71+
{
72+
var peers = ScanFixtures ();
73+
74+
var mainActivity = FindByJavaName (peers, "my/app/MainActivity");
75+
Assert.False (mainActivity.DoNotGenerateAcw, "MainActivity should not have DoNotGenerateAcw");
76+
}
77+
78+
[Fact]
79+
public void Scan_ActivityType_IsUnconditional ()
80+
{
81+
var peers = ScanFixtures ();
82+
var mainActivity = FindByJavaName (peers, "my/app/MainActivity");
83+
Assert.True (mainActivity.IsUnconditional, "MainActivity with [Activity] should be unconditional");
84+
}
85+
86+
[Fact]
87+
public void Scan_ServiceType_IsUnconditional ()
88+
{
89+
var peers = ScanFixtures ();
90+
var service = FindByJavaName (peers, "my/app/MyService");
91+
Assert.True (service.IsUnconditional, "MyService with [Service] should be unconditional");
92+
}
93+
94+
[Fact]
95+
public void Scan_BroadcastReceiverType_IsUnconditional ()
96+
{
97+
var peers = ScanFixtures ();
98+
var receiver = FindByJavaName (peers, "my/app/MyReceiver");
99+
Assert.True (receiver.IsUnconditional, "MyReceiver with [BroadcastReceiver] should be unconditional");
100+
}
101+
102+
[Fact]
103+
public void Scan_ContentProviderType_IsUnconditional ()
104+
{
105+
var peers = ScanFixtures ();
106+
var provider = FindByJavaName (peers, "my/app/MyProvider");
107+
Assert.True (provider.IsUnconditional, "MyProvider with [ContentProvider] should be unconditional");
108+
}
109+
110+
[Fact]
111+
public void Scan_TypeWithoutComponentAttribute_IsTrimmable ()
112+
{
113+
var peers = ScanFixtures ();
114+
var helper = FindByJavaName (peers, "my/app/MyHelper");
115+
Assert.False (helper.IsUnconditional, "MyHelper without component attr should be trimmable");
116+
}
117+
118+
[Fact]
119+
public void Scan_McwBinding_IsTrimmable ()
120+
{
121+
var peers = ScanFixtures ();
122+
var activity = FindByJavaName (peers, "android/app/Activity");
123+
Assert.False (activity.IsUnconditional, "MCW Activity should be trimmable (no component attr on MCW type)");
124+
}
125+
126+
[Fact]
127+
public void Scan_InterfaceType_IsMarkedAsInterface ()
128+
{
129+
var peers = ScanFixtures ();
130+
var listener = FindByManagedName (peers, "Android.Views.IOnClickListener");
131+
Assert.True (listener.IsInterface, "IOnClickListener should be marked as interface");
132+
}
133+
134+
[Fact]
135+
public void Scan_InvokerTypes_AreIncluded ()
136+
{
137+
var peers = ScanFixtures ();
138+
var invoker = peers.FirstOrDefault (p => p.ManagedTypeName == "Android.Views.IOnClickListenerInvoker");
139+
Assert.NotNull (invoker);
140+
Assert.True (invoker.DoNotGenerateAcw, "Invoker should have DoNotGenerateAcw=true");
141+
Assert.Equal ("android/view/View$OnClickListener", invoker.JavaName);
142+
}
143+
144+
[Fact]
145+
public void Scan_GenericType_IsGenericDefinition ()
146+
{
147+
var peers = ScanFixtures ();
148+
var generic = FindByJavaName (peers, "my/app/GenericHolder");
149+
Assert.True (generic.IsGenericDefinition, "GenericHolder<T> should be marked as generic definition");
150+
}
151+
152+
[Fact]
153+
public void Scan_AbstractType_IsMarkedAbstract ()
154+
{
155+
var peers = ScanFixtures ();
156+
var abstractBase = FindByJavaName (peers, "my/app/AbstractBase");
157+
Assert.True (abstractBase.IsAbstract, "AbstractBase should be marked as abstract");
158+
}
159+
160+
[Fact]
161+
public void Scan_MarshalMethods_Collected ()
162+
{
163+
var peers = ScanFixtures ();
164+
var activity = FindByJavaName (peers, "android/app/Activity");
165+
Assert.NotEmpty (activity.MarshalMethods);
166+
}
167+
168+
[Fact]
169+
public void Scan_UserTypeOverride_CollectsMarshalMethods ()
170+
{
171+
var peers = ScanFixtures ();
172+
var mainActivity = FindByJavaName (peers, "my/app/MainActivity");
173+
Assert.NotEmpty (mainActivity.MarshalMethods);
174+
175+
var onCreate = mainActivity.MarshalMethods.FirstOrDefault (m => m.ManagedMethodName == "OnCreate");
176+
Assert.NotNull (onCreate);
177+
Assert.Equal ("(Landroid/os/Bundle;)V", onCreate.JniSignature);
178+
}
179+
180+
[Fact]
181+
public void Scan_TypeWithOwnActivationCtor_ResolvesToSelf ()
182+
{
183+
var peers = ScanFixtures ();
184+
var activity = FindByJavaName (peers, "android/app/Activity");
185+
Assert.NotNull (activity.ActivationCtor);
186+
Assert.Equal ("Android.App.Activity", activity.ActivationCtor.DeclaringTypeName);
187+
Assert.Equal (ActivationCtorStyle.XamarinAndroid, activity.ActivationCtor.Style);
188+
}
189+
190+
[Fact]
191+
public void Scan_TypeWithoutOwnActivationCtor_InheritsFromBase ()
192+
{
193+
var peers = ScanFixtures ();
194+
var simpleActivity = FindByJavaName (peers, "my/app/SimpleActivity");
195+
Assert.NotNull (simpleActivity.ActivationCtor);
196+
Assert.Equal ("Android.App.Activity", simpleActivity.ActivationCtor.DeclaringTypeName);
197+
Assert.Equal (ActivationCtorStyle.XamarinAndroid, simpleActivity.ActivationCtor.Style);
198+
}
199+
200+
[Fact]
201+
public void Scan_TypeWithOwnActivationCtor_DoesNotLookAtBase ()
202+
{
203+
var peers = ScanFixtures ();
204+
var mainActivity = FindByJavaName (peers, "my/app/MainActivity");
205+
Assert.NotNull (mainActivity.ActivationCtor);
206+
Assert.Equal ("Android.App.Activity", mainActivity.ActivationCtor.DeclaringTypeName);
207+
}
208+
209+
[Fact]
210+
public void Scan_AllTypes_HaveAssemblyName ()
211+
{
212+
var peers = ScanFixtures ();
213+
Assert.All (peers, peer =>
214+
Assert.False (string.IsNullOrEmpty (peer.AssemblyName),
215+
$"Type {peer.ManagedTypeName} should have assembly name"));
216+
}
217+
218+
[Fact]
219+
public void Scan_InvokerSharesJavaNameWithInterface ()
220+
{
221+
var peers = ScanFixtures ();
222+
var clickListenerPeers = peers.Where (p => p.JavaName == "android/view/View$OnClickListener").ToList ();
223+
// Interface + Invoker share the same JNI name (this is expected — they're aliases)
224+
Assert.Equal (2, clickListenerPeers.Count);
225+
Assert.Contains (clickListenerPeers, p => p.IsInterface);
226+
Assert.Contains (clickListenerPeers, p => p.DoNotGenerateAcw);
227+
}
228+
229+
[Fact]
230+
public void Scan_ActivityBaseJavaName_IsJavaLangObject ()
231+
{
232+
var peers = ScanFixtures ();
233+
var activity = FindByJavaName (peers, "android/app/Activity");
234+
Assert.Equal ("java/lang/Object", activity.BaseJavaName);
235+
}
236+
237+
[Fact]
238+
public void Scan_MainActivityBaseJavaName_IsActivity ()
239+
{
240+
var peers = ScanFixtures ();
241+
var mainActivity = FindByJavaName (peers, "my/app/MainActivity");
242+
Assert.Equal ("android/app/Activity", mainActivity.BaseJavaName);
243+
}
244+
245+
[Fact]
246+
public void Scan_JavaLangObjectBaseJavaName_IsNull ()
247+
{
248+
var peers = ScanFixtures ();
249+
var jlo = FindByJavaName (peers, "java/lang/Object");
250+
Assert.Null (jlo.BaseJavaName);
251+
}
252+
253+
[Fact]
254+
public void Scan_TypeImplementingInterface_HasInterfaceJavaNames ()
255+
{
256+
var peers = ScanFixtures ();
257+
var clickable = FindByJavaName (peers, "my/app/ClickableView");
258+
Assert.Contains ("android/view/View$OnClickListener", clickable.ImplementedInterfaceJavaNames);
259+
}
260+
261+
[Fact]
262+
public void Scan_TypeNotImplementingInterface_HasEmptyList ()
263+
{
264+
var peers = ScanFixtures ();
265+
var helper = FindByJavaName (peers, "my/app/MyHelper");
266+
Assert.Empty (helper.ImplementedInterfaceJavaNames);
267+
}
268+
}

0 commit comments

Comments
 (0)