-
Notifications
You must be signed in to change notification settings - Fork 568
Add unit tests for Xamarin.Android.Tools.Aidl.CSharpCodeGenerator #11460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
simonrozsival
merged 5 commits into
main
from
copilot/add-unit-tests-for-csharpcodegenerator
May 27, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
e31b906
Initial plan
Copilot a3702b3
Add CSharpCodeGenerator unit tests covering parcelable, multi-interfa…
Copilot 9cc05f1
Remove incomplete issue URL placeholder from test comment
Copilot 764685f
Address review: drop unused import, document oneway snapshot
jonathanpeppers de9b054
Link generator-quirk tests to tracking issues #11507 and #11508
jonathanpeppers File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
tests/Xamarin.Android.Tools.Aidl-Tests/CSharpCodeGeneratorTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| using System; | ||
| using System.Linq; | ||
| using NUnit.Framework; | ||
| using Xamarin.Android.Tools.Aidl; | ||
|
|
||
| namespace Xamarin.Android.Tools.Aidl_Tests | ||
| { | ||
| // Tests targeting specific code paths of Xamarin.Android.Tools.Aidl.CSharpCodeGenerator. | ||
| // These exist so we don't need to go through the MSBuild integration tests in AidlTest.cs | ||
| // to validate generator behavior. | ||
| [TestFixture] | ||
| public class CSharpCodeGeneratorTests : AidlCompilerTestBase | ||
| { | ||
| [Test] | ||
| public void ParcelableIgnore () => RunTest (nameof (ParcelableIgnore), ParcelableHandling.Ignore); | ||
|
|
||
| [Test] | ||
| public void ParcelableStub () => RunTest (nameof (ParcelableStub), ParcelableHandling.Stub); | ||
|
|
||
| [Test] | ||
| public void ParcelableError () | ||
| { | ||
| const string input = @"package com.xamarin.test; | ||
| parcelable MyData; | ||
| "; | ||
| var compiler = new AidlCompiler (); | ||
| Assert.Throws<InvalidOperationException> (() => | ||
| compiler.Run (input, out _, parcelableHandling: ParcelableHandling.Error)); | ||
| } | ||
|
|
||
| [Test] | ||
| public void MultipleInterfaces () => RunTest (nameof (MultipleInterfaces)); | ||
|
|
||
| // NOTE: This test intentionally snapshots the *current* generator behavior for `oneway` methods. | ||
| // The generated Proxy still allocates a reply Parcel and calls `__reply.ReadException ()`, which | ||
| // does not match true AIDL oneway semantics. Tracked by https://github.com/dotnet/android/issues/11507. | ||
| [Test] | ||
| public void OnewayMethods () => RunTest (nameof (OnewayMethods)); | ||
|
|
||
| // NOTE: The golden output for this test also captures a pre-existing bug where the generated | ||
| // Proxy void method allocates `__reply` but never recycles it, leaking Parcel instances. | ||
| // Tracked by https://github.com/dotnet/android/issues/11508. | ||
| [Test] | ||
| public void IBinderTypes () => RunTest (nameof (IBinderTypes)); | ||
|
|
||
| [Test] | ||
| public void CharSequenceType () => RunTest (nameof (CharSequenceType)); | ||
|
|
||
| [Test] | ||
| public void ParseErrorIsReported () | ||
| { | ||
| const string input = "this is not valid aidl @@@"; | ||
|
|
||
| var compiler = new AidlCompiler (); | ||
| var results = compiler.Run (input, out var output); | ||
|
|
||
| Assert.IsNull (output, "Output should be null when the input fails to parse."); | ||
| Assert.IsTrue (results.LogMessages.Any (), "Parser error messages should be reported."); | ||
| } | ||
| } | ||
| } | ||
151 changes: 151 additions & 0 deletions
151
tests/Xamarin.Android.Tools.Aidl-Tests/TestData/CharSequenceType.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| package com.xamarin.test; | ||
|
|
||
| import java.lang; | ||
|
|
||
| interface ITest { | ||
| CharSequence getLabel (); | ||
| void setLabel (in CharSequence label); | ||
| } | ||
|
|
||
| #### | ||
|
|
||
| // This file is automatically generated and not supposed to be modified. | ||
| using System; | ||
| using Boolean = System.Boolean; | ||
| using String = System.String; | ||
| using List = Android.Runtime.JavaList; | ||
| using Map = Android.Runtime.JavaDictionary; | ||
| using Java; | ||
|
|
||
| namespace Com.Xamarin.Test | ||
| { | ||
| public interface ITest : global::Android.OS.IInterface | ||
| { | ||
| Java.Lang.ICharSequence GetLabel (); | ||
| void SetLabel (Java.Lang.ICharSequence label); | ||
| } | ||
|
|
||
| public abstract class ITestStub : global::Android.OS.Binder, global::Android.OS.IInterface, Com.Xamarin.Test.ITest | ||
| { | ||
| const string descriptor = "com.xamarin.test.ITest"; | ||
| public ITestStub () | ||
| { | ||
| this.AttachInterface (this, descriptor); | ||
| } | ||
|
|
||
| public static Com.Xamarin.Test.ITest AsInterface (global::Android.OS.IBinder obj) | ||
| { | ||
| if (obj == null) | ||
| return null; | ||
| var iin = (global::Android.OS.IInterface) obj.QueryLocalInterface (descriptor); | ||
| if (iin != null && iin is Com.Xamarin.Test.ITest) | ||
| return (Com.Xamarin.Test.ITest) iin; | ||
| return new Proxy (obj); | ||
| } | ||
|
|
||
| public global::Android.OS.IBinder AsBinder () | ||
| { | ||
| return this; | ||
| } | ||
|
|
||
| protected override bool OnTransact (int code, global::Android.OS.Parcel data, global::Android.OS.Parcel reply, int flags) | ||
| { | ||
| switch (code) { | ||
| case global::Android.OS.BinderConsts.InterfaceTransaction: | ||
| reply.WriteString (descriptor); | ||
| return true; | ||
|
|
||
| case TransactionGetLabel: { | ||
| data.EnforceInterface (descriptor); | ||
| var result = this.GetLabel (); | ||
| reply.WriteNoException (); | ||
| if (result != null) { reply.WriteInt (1); global::Android.Text.TextUtils.WriteToParcel (result, reply, global::Android.OS.ParcelableWriteFlags.ReturnValue); } else reply.WriteInt (0); | ||
| return true; | ||
| } | ||
|
|
||
| case TransactionSetLabel: { | ||
| data.EnforceInterface (descriptor); | ||
| Java.Lang.ICharSequence arg0 = default (Java.Lang.ICharSequence); | ||
| arg0 = data.ReadInt () != 0 ? (global::Java.Lang.ICharSequence) global::Android.Text.TextUtils.CharSequenceCreator.CreateFromParcel (data) : null; | ||
| this.SetLabel (arg0); | ||
| reply.WriteNoException (); | ||
| return true; | ||
| } | ||
|
|
||
| } | ||
| return base.OnTransact (code, data, reply, flags); | ||
| } | ||
|
|
||
| public class Proxy : Java.Lang.Object, Com.Xamarin.Test.ITest | ||
| { | ||
| global::Android.OS.IBinder remote; | ||
|
|
||
| public Proxy (global::Android.OS.IBinder remote) | ||
| { | ||
| this.remote = remote; | ||
| } | ||
|
|
||
| public global::Android.OS.IBinder AsBinder () | ||
| { | ||
| return remote; | ||
| } | ||
|
|
||
| public string GetInterfaceDescriptor () | ||
| { | ||
| return descriptor; | ||
| } | ||
|
|
||
| public Java.Lang.ICharSequence GetLabel () | ||
| { | ||
| global::Android.OS.Parcel __data = global::Android.OS.Parcel.Obtain (); | ||
|
|
||
| global::Android.OS.Parcel __reply = global::Android.OS.Parcel.Obtain (); | ||
| Java.Lang.ICharSequence __result = default (Java.Lang.ICharSequence); | ||
|
|
||
| try { | ||
| __data.WriteInterfaceToken (descriptor); | ||
| remote.Transact (ITestStub.TransactionGetLabel, __data, __reply, 0); | ||
| __reply.ReadException (); | ||
| __result = __reply.ReadInt () != 0 ? (global::Java.Lang.ICharSequence) global::Android.Text.TextUtils.CharSequenceCreator.CreateFromParcel (__reply) : null; | ||
|
|
||
| } finally { | ||
| __reply.Recycle (); | ||
| __data.Recycle (); | ||
| } | ||
| return __result; | ||
|
|
||
| } | ||
|
|
||
|
|
||
| public void SetLabel (Java.Lang.ICharSequence label) | ||
| { | ||
| global::Android.OS.Parcel __data = global::Android.OS.Parcel.Obtain (); | ||
|
|
||
| global::Android.OS.Parcel __reply = global::Android.OS.Parcel.Obtain (); | ||
|
|
||
| try { | ||
| __data.WriteInterfaceToken (descriptor); | ||
| if (label != null) { __data.WriteInt (1); global::Android.Text.TextUtils.WriteToParcel (label, __data, global::Android.OS.ParcelableWriteFlags.None); } else __data.WriteInt (0); | ||
| remote.Transact (ITestStub.TransactionSetLabel, __data, __reply, 0); | ||
| __reply.ReadException (); | ||
|
|
||
| } finally { | ||
| __data.Recycle (); | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| } | ||
|
|
||
| internal const int TransactionGetLabel = global::Android.OS.Binder.InterfaceConsts.FirstCallTransaction + 0; | ||
|
|
||
| internal const int TransactionSetLabel = global::Android.OS.Binder.InterfaceConsts.FirstCallTransaction + 1; | ||
|
|
||
| public abstract Java.Lang.ICharSequence GetLabel (); | ||
|
|
||
| public abstract void SetLabel (Java.Lang.ICharSequence label); | ||
|
|
||
| } | ||
| } | ||
|
|
151 changes: 151 additions & 0 deletions
151
tests/Xamarin.Android.Tools.Aidl-Tests/TestData/IBinderTypes.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| package com.xamarin.test; | ||
|
|
||
| import android.os; | ||
|
|
||
| interface ITest { | ||
| IBinder getBinder (); | ||
| void setBinder (in IBinder binder); | ||
| } | ||
|
|
||
| #### | ||
|
|
||
| // This file is automatically generated and not supposed to be modified. | ||
| using System; | ||
| using Boolean = System.Boolean; | ||
| using String = System.String; | ||
| using List = Android.Runtime.JavaList; | ||
| using Map = Android.Runtime.JavaDictionary; | ||
| using Android; | ||
|
|
||
| namespace Com.Xamarin.Test | ||
| { | ||
| public interface ITest : global::Android.OS.IInterface | ||
| { | ||
| Android.OS.IBinder GetBinder (); | ||
| void SetBinder (Android.OS.IBinder binder); | ||
| } | ||
|
|
||
| public abstract class ITestStub : global::Android.OS.Binder, global::Android.OS.IInterface, Com.Xamarin.Test.ITest | ||
| { | ||
| const string descriptor = "com.xamarin.test.ITest"; | ||
| public ITestStub () | ||
| { | ||
| this.AttachInterface (this, descriptor); | ||
| } | ||
|
|
||
| public static Com.Xamarin.Test.ITest AsInterface (global::Android.OS.IBinder obj) | ||
| { | ||
| if (obj == null) | ||
| return null; | ||
| var iin = (global::Android.OS.IInterface) obj.QueryLocalInterface (descriptor); | ||
| if (iin != null && iin is Com.Xamarin.Test.ITest) | ||
| return (Com.Xamarin.Test.ITest) iin; | ||
| return new Proxy (obj); | ||
| } | ||
|
|
||
| public global::Android.OS.IBinder AsBinder () | ||
| { | ||
| return this; | ||
| } | ||
|
|
||
| protected override bool OnTransact (int code, global::Android.OS.Parcel data, global::Android.OS.Parcel reply, int flags) | ||
| { | ||
| switch (code) { | ||
| case global::Android.OS.BinderConsts.InterfaceTransaction: | ||
| reply.WriteString (descriptor); | ||
| return true; | ||
|
|
||
| case TransactionGetBinder: { | ||
| data.EnforceInterface (descriptor); | ||
| var result = this.GetBinder (); | ||
| reply.WriteNoException (); | ||
| reply.WriteStrongBinder (result); | ||
| return true; | ||
| } | ||
|
|
||
| case TransactionSetBinder: { | ||
| data.EnforceInterface (descriptor); | ||
| Android.OS.IBinder arg0 = default (Android.OS.IBinder); | ||
| arg0 = data.ReadStrongBinder (); | ||
| this.SetBinder (arg0); | ||
| reply.WriteNoException (); | ||
| return true; | ||
| } | ||
|
|
||
| } | ||
| return base.OnTransact (code, data, reply, flags); | ||
| } | ||
|
|
||
| public class Proxy : Java.Lang.Object, Com.Xamarin.Test.ITest | ||
| { | ||
| global::Android.OS.IBinder remote; | ||
|
|
||
| public Proxy (global::Android.OS.IBinder remote) | ||
| { | ||
| this.remote = remote; | ||
| } | ||
|
|
||
| public global::Android.OS.IBinder AsBinder () | ||
| { | ||
| return remote; | ||
| } | ||
|
|
||
| public string GetInterfaceDescriptor () | ||
| { | ||
| return descriptor; | ||
| } | ||
|
|
||
| public Android.OS.IBinder GetBinder () | ||
| { | ||
| global::Android.OS.Parcel __data = global::Android.OS.Parcel.Obtain (); | ||
|
|
||
| global::Android.OS.Parcel __reply = global::Android.OS.Parcel.Obtain (); | ||
| Android.OS.IBinder __result = default (Android.OS.IBinder); | ||
|
|
||
| try { | ||
| __data.WriteInterfaceToken (descriptor); | ||
| remote.Transact (ITestStub.TransactionGetBinder, __data, __reply, 0); | ||
| __reply.ReadException (); | ||
| __result = __reply.ReadStrongBinder (); | ||
|
|
||
| } finally { | ||
| __reply.Recycle (); | ||
| __data.Recycle (); | ||
| } | ||
| return __result; | ||
|
|
||
| } | ||
|
|
||
|
|
||
| public void SetBinder (Android.OS.IBinder binder) | ||
| { | ||
| global::Android.OS.Parcel __data = global::Android.OS.Parcel.Obtain (); | ||
|
|
||
| global::Android.OS.Parcel __reply = global::Android.OS.Parcel.Obtain (); | ||
|
|
||
| try { | ||
| __data.WriteInterfaceToken (descriptor); | ||
| __data.WriteStrongBinder (binder); | ||
| remote.Transact (ITestStub.TransactionSetBinder, __data, __reply, 0); | ||
| __reply.ReadException (); | ||
|
|
||
| } finally { | ||
|
jonathanpeppers marked this conversation as resolved.
|
||
| __data.Recycle (); | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
| } | ||
|
|
||
| internal const int TransactionGetBinder = global::Android.OS.Binder.InterfaceConsts.FirstCallTransaction + 0; | ||
|
|
||
| internal const int TransactionSetBinder = global::Android.OS.Binder.InterfaceConsts.FirstCallTransaction + 1; | ||
|
|
||
| public abstract Android.OS.IBinder GetBinder (); | ||
|
|
||
| public abstract void SetBinder (Android.OS.IBinder binder); | ||
|
|
||
| } | ||
| } | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.