Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1563,6 +1563,80 @@ public void UnsupportedOSPlatformIgnoresPropertyOverrides ()
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]", ifaceActual, "Should not contain UnsupportedOSPlatform on interface property override!");
}

[Test]
public void UnsupportedOSPlatformIgnoresPropertySetterOverridesWhenBaseHasGetterOnly ()
{
// Given:
// public class AdapterView {
// public Object getAdapter () { ... }
// }
// public class ListView : AdapterView {
// public Object getAdapter () { ... } // removed-since = 15
// public void setAdapter (Object value) { ... } // removed-since = 15
// }
// We should not write [UnsupportedOSPlatform] on ListView.Adapter.set because the base property (via getter) isn't "removed".
var xml = @$"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='android.widget' jni-name='android/widget'>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='AdapterView' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' />
</class>
<class abstract='false' deprecated='not deprecated' extends='android.widget.AdapterView' extends-generic-aware='android.widget.AdapterView' final='false' name='ListView' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15' />
<method abstract='false' deprecated='not deprecated' final='false' name='setAdapter' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15'>
<parameter name='value' type='java.lang.Object' />
</method>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var klass = gens.Single (g => g.Name == "ListView");
var actual = GetGeneratedTypeOutput (klass);

// Neither the getter nor the setter should have [UnsupportedOSPlatform] because the base property (getter) isn't removed
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android15.0\")]", actual, "Should not contain UnsupportedOSPlatform on property setter when base has getter only!");
}

[Test]
public void UnsupportedOSPlatformIgnoresStandaloneSetterMethodWhenBaseHasGetterOnly ()
{
// Given:
// public class AdapterView {
// public Object getAdapter () { ... }
// }
// public class ListView : AdapterView {
// // no getAdapter override
// public void setAdapter (Object value) { ... } // removed-since = 15
// }
// We should not write [UnsupportedOSPlatform] on ListView.SetAdapter because the base property (via getter) isn't "removed".
// The setAdapter remains a standalone method because there's no getAdapter to pair with in the derived class.
var xml = @$"<api>
<package name='java.lang' jni-name='java/lang'>
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
</package>
<package name='android.widget' jni-name='android/widget'>
<class abstract='false' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' final='false' name='AdapterView' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='getAdapter' bridge='false' native='false' return='java.lang.Object' static='false' synchronized='false' synthetic='false' visibility='public' />
</class>
<class abstract='false' deprecated='not deprecated' extends='android.widget.AdapterView' extends-generic-aware='android.widget.AdapterView' final='false' name='ListView' static='false' visibility='public'>
<method abstract='false' deprecated='not deprecated' final='false' name='setAdapter' bridge='false' native='false' return='void' static='false' synchronized='false' synthetic='false' visibility='public' removed-since='15'>
<parameter name='value' type='java.lang.Object' />
</method>
</class>
</package>
</api>";

var gens = ParseApiDefinition (xml);
var klass = gens.Single (g => g.Name == "ListView");
var actual = GetGeneratedTypeOutput (klass);

// The standalone setter method should not have [UnsupportedOSPlatform] because the base property (getter) isn't removed
StringAssert.DoesNotContain ("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android15.0\")]", actual, "Should not contain UnsupportedOSPlatform on standalone setter method when base has getter only!");
}

[Test]
public void StringPropertyOverride ([Values ("true", "false")] string final)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,32 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
prop.Setter.ApiRemovedSince = default;
shouldBreak = true;
}
} else if (prop.Setter != null && prop.Setter.ApiRemovedSince > 0 && baseProp.Setter == null && baseProp.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
// Base has getter-only property; setter in derived should not be marked removed
prop.Setter.ApiRemovedSince = default;
shouldBreak = true;
}
if (shouldBreak) {
break;
}
}
}

// Process standalone setter methods (setXxx) that correspond to base class properties.
// If the base property getter isn't removed, the setter shouldn't be either.
foreach (var m in Methods.Where (m => !m.IsStatic && !m.IsInterfaceDefaultMethod && m.ApiRemovedSince > 0)) {
if (!m.JavaName.StartsWith ("set", StringComparison.Ordinal) || m.Parameters.Count != 1 || m.RetVal.JavaName != "void")
continue;
var propertyName = m.JavaName.Substring (3);
for (var bt = GetBaseGen (opt); bt != null; bt = bt.GetBaseGen (opt)) {
var baseProp = bt.Properties.FirstOrDefault (p => p.Getter?.JavaName == "get" + propertyName);
if (baseProp?.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
m.ApiRemovedSince = default;
break;
}
}
}

// Process interface inheritance for both regular and default interface methods
if (this is InterfaceGen currentInterface) {
// For interfaces, check all base interfaces (interfaces that this interface implements/extends)
Expand Down Expand Up @@ -419,6 +438,10 @@ public void FixupMethodOverrides (CodeGenerationOptions opt)
prop.Setter.ApiRemovedSince = default;
shouldBreak = true;
}
} else if (prop.Setter != null && prop.Setter.ApiRemovedSince > 0 && baseProp.Setter == null && baseProp.Getter != null && baseProp.Getter.ApiRemovedSince == 0) {
// Base has getter-only property; setter in derived should not be marked removed
prop.Setter.ApiRemovedSince = default;
shouldBreak = true;
}
if (shouldBreak) {
break;
Expand Down