Skip to content

Commit fc89f74

Browse files
Preserve coverage surface metadata
1 parent 8efd9a4 commit fc89f74

4 files changed

Lines changed: 173 additions & 4 deletions

File tree

scripts/FirebaseBindingAudit.Tests/BindingSurfaceCoverageBuilderTests.cs

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,141 @@ public partial class AuthManualSurface
4848
}
4949
}
5050

51+
[Fact]
52+
public void Build_PreservesManualExportStaticness()
53+
{
54+
var repoRoot = Path.Combine(Path.GetTempPath(), $"firebase-binding-surface-builder-{Guid.NewGuid():N}");
55+
56+
try
57+
{
58+
var sourceDirectory = Path.Combine(repoRoot, "source", "Firebase", "Auth");
59+
Directory.CreateDirectory(sourceDirectory);
60+
File.WriteAllText(
61+
Path.Combine(sourceDirectory, "ApiDefinition.cs"),
62+
"""
63+
namespace Firebase.Auth;
64+
65+
[BaseType(typeof(NSObject), Name = "FIRAuth")]
66+
public interface Auth
67+
{
68+
[Static]
69+
[Async]
70+
[Export("fetchSignInMethodsForEmail:completion:")]
71+
void FetchSignInMethods(string email, Action completion);
72+
}
73+
""");
74+
75+
var document = new BindingSurfaceCoverageBuilder(CreateConfiguration()).Build(
76+
repoRoot,
77+
CreateManifest(),
78+
"Auth");
79+
80+
var surface = Assert.Single(
81+
document.Targets.Single().Surfaces,
82+
static surface => surface.MemberName == "FetchSignInMethods");
83+
84+
Assert.True(surface.IsStatic);
85+
var nativeSelector = Assert.Single(surface.NativeSelectors);
86+
Assert.Equal("fetchSignInMethodsForEmail:completion:", nativeSelector.Selector);
87+
Assert.True(nativeSelector.IsStatic);
88+
}
89+
finally
90+
{
91+
if (Directory.Exists(repoRoot))
92+
{
93+
Directory.Delete(repoRoot, recursive: true);
94+
}
95+
}
96+
}
97+
98+
[Fact]
99+
public void Build_SynthesizesNamedIsPropertySetterSelector()
100+
{
101+
var repoRoot = Path.Combine(Path.GetTempPath(), $"firebase-binding-surface-builder-{Guid.NewGuid():N}");
102+
103+
try
104+
{
105+
var sourceDirectory = Path.Combine(repoRoot, "source", "Firebase", "Auth");
106+
Directory.CreateDirectory(sourceDirectory);
107+
File.WriteAllText(
108+
Path.Combine(sourceDirectory, "ApiDefinition.cs"),
109+
"""
110+
namespace Firebase.Auth;
111+
112+
[BaseType(typeof(NSObject), Name = "FIRAuth")]
113+
public interface Auth
114+
{
115+
[Export("isTokenAutoRefreshEnabled")]
116+
bool IsTokenAutoRefreshEnabled { get; set; }
117+
}
118+
""");
119+
120+
var document = new BindingSurfaceCoverageBuilder(CreateConfiguration()).Build(
121+
repoRoot,
122+
CreateManifest(),
123+
"Auth");
124+
125+
var surface = Assert.Single(
126+
document.Targets.Single().Surfaces,
127+
static surface => surface.MemberName == "IsTokenAutoRefreshEnabled");
128+
129+
Assert.Equal(
130+
["isTokenAutoRefreshEnabled", "setIsTokenAutoRefreshEnabled:"],
131+
surface.NativeSelectors.Select(static selector => selector.Selector).ToArray());
132+
}
133+
finally
134+
{
135+
if (Directory.Exists(repoRoot))
136+
{
137+
Directory.Delete(repoRoot, recursive: true);
138+
}
139+
}
140+
}
141+
142+
[Fact]
143+
public void Build_UsesGetterBindForBooleanGetterSelector()
144+
{
145+
var repoRoot = Path.Combine(Path.GetTempPath(), $"firebase-binding-surface-builder-{Guid.NewGuid():N}");
146+
147+
try
148+
{
149+
var sourceDirectory = Path.Combine(repoRoot, "source", "Firebase", "Auth");
150+
Directory.CreateDirectory(sourceDirectory);
151+
File.WriteAllText(
152+
Path.Combine(sourceDirectory, "ApiDefinition.cs"),
153+
"""
154+
namespace Firebase.Auth;
155+
156+
[BaseType(typeof(NSObject), Name = "FIRAuth")]
157+
public interface Auth
158+
{
159+
[Export("tokenAutoRefreshEnabled")]
160+
bool TokenAutoRefreshEnabled { [Bind("isTokenAutoRefreshEnabled")] get; set; }
161+
}
162+
""");
163+
164+
var document = new BindingSurfaceCoverageBuilder(CreateConfiguration()).Build(
165+
repoRoot,
166+
CreateManifest(),
167+
"Auth");
168+
169+
var surface = Assert.Single(
170+
document.Targets.Single().Surfaces,
171+
static surface => surface.MemberName == "TokenAutoRefreshEnabled");
172+
173+
Assert.Equal(
174+
["isTokenAutoRefreshEnabled", "setTokenAutoRefreshEnabled:"],
175+
surface.NativeSelectors.Select(static selector => selector.Selector).ToArray());
176+
}
177+
finally
178+
{
179+
if (Directory.Exists(repoRoot))
180+
{
181+
Directory.Delete(repoRoot, recursive: true);
182+
}
183+
}
184+
}
185+
51186
[Fact]
52187
public void Build_RecordsPublicHelperDelegatesConstructorsAndIndexers()
53188
{
@@ -122,7 +257,7 @@ public Auth(string name)
122257
private static AuditConfiguration CreateConfiguration(string[]? helperFiles = null) =>
123258
new()
124259
{
125-
ManualAttributes = ["Wrap"],
260+
ManualAttributes = ["Wrap", "Async"],
126261
BindingAttributes = ["Export", "Field", "Notification"],
127262
Targets =
128263
[

scripts/FirebaseBindingAudit/BindingModel.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ internal sealed record ManualSurfaceItem(
7979
string MatchTypeKey,
8080
string? MatchMemberKey,
8181
string? MemberName,
82+
bool IsStatic,
8283
IReadOnlyList<string> ParameterTypes,
8384
string? ReturnType,
8485
string Signature,
@@ -291,6 +292,7 @@ private static void AddManualTypeItems(
291292
MatchTypeKey: typeMatchKey,
292293
MatchMemberKey: null,
293294
MemberName: null,
295+
IsStatic: false,
294296
ParameterTypes: [],
295297
ReturnType: null,
296298
Signature: $"{containerKind} {typeName}",
@@ -306,6 +308,7 @@ private static void AddManualTypeItems(
306308
MatchTypeKey: typeMatchKey,
307309
MatchMemberKey: member.Key,
308310
MemberName: member.Name,
311+
IsStatic: member.IsStatic,
309312
ParameterTypes: member.Parameters.Select(static parameter => parameter.Type).ToList(),
310313
ReturnType: member.ReturnType,
311314
Signature: member.Signature,
@@ -337,6 +340,8 @@ private void ParseMethod(
337340
MatchTypeKey: containingTypeMatchKey,
338341
MatchMemberKey: bindingAttribute is null ? null : CreateMemberKey(bindingAttribute, bindingValue, methodDeclaration.Identifier.Text, methodDeclaration.ParameterList.Parameters.Count),
339342
MemberName: methodDeclaration.Identifier.Text,
343+
IsStatic: methodDeclaration.Modifiers.Any(static token => token.IsKind(SyntaxKind.StaticKeyword)) ||
344+
HasAttribute(methodDeclaration.AttributeLists, "Static"),
340345
ParameterTypes: methodDeclaration.ParameterList.Parameters.Select(static parameter => NormalizeType(parameter.Type)).ToList(),
341346
ReturnType: NormalizeType(methodDeclaration.ReturnType),
342347
Signature: signature,
@@ -400,6 +405,9 @@ private void ParseProperty(
400405
MatchTypeKey: containingTypeMatchKey,
401406
MatchMemberKey: bindingAttribute is null ? null : CreateMemberKey(bindingAttribute, bindingValue, propertyDeclaration.Identifier.Text, 0),
402407
MemberName: propertyDeclaration.Identifier.Text,
408+
IsStatic: propertyDeclaration.Modifiers.Any(static token => token.IsKind(SyntaxKind.StaticKeyword)) ||
409+
HasAttribute(propertyDeclaration.AttributeLists, "Static") ||
410+
string.Equals(bindingAttribute, "Field", StringComparison.Ordinal),
403411
ParameterTypes: [],
404412
ReturnType: NormalizeType(propertyDeclaration.Type),
405413
Signature: signature,
@@ -456,6 +464,7 @@ private void ParseDelegate(
456464
MatchTypeKey: delegateDeclaration.Identifier.Text,
457465
MatchMemberKey: null,
458466
MemberName: null,
467+
IsStatic: false,
459468
ParameterTypes: delegateDeclaration.ParameterList.Parameters.Select(static parameter => NormalizeType(parameter.Type)).ToList(),
460469
ReturnType: NormalizeType(delegateDeclaration.ReturnType),
461470
Signature: BuildDelegateSignature(delegateDeclaration),
@@ -492,6 +501,7 @@ private void ParseEnum(
492501
MatchTypeKey: enumDeclaration.Identifier.Text,
493502
MatchMemberKey: null,
494503
MemberName: null,
504+
IsStatic: false,
495505
ParameterTypes: [],
496506
ReturnType: null,
497507
Signature: $"enum {enumDeclaration.Identifier.Text}",

scripts/FirebaseBindingAudit/BindingSurfaceCoverage.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Text;
12
using System.Text.Json;
23
using System.Text.Json.Serialization;
34
using System.Xml.Linq;
@@ -287,11 +288,15 @@ private static XDocument BuildProps(
287288
.Distinct(StringComparer.Ordinal)
288289
.OrderBy(static assemblyName => assemblyName, StringComparer.Ordinal)
289290
.ToList();
291+
var defineConstants = string.Join(
292+
";",
293+
new[] { "$(DefineConstants)", "ENABLE_BINDING_SURFACE_COVERAGE" }
294+
.Concat(document.Targets.Select(static target => CreateTargetCompileConstant(target.Id))));
290295

291296
var project = new XElement("Project",
292297
new XElement("PropertyGroup",
293298
new XElement("BindingSurfaceCoverageTarget", selectedTarget),
294-
new XElement("DefineConstants", "$(DefineConstants);ENABLE_BINDING_SURFACE_COVERAGE")),
299+
new XElement("DefineConstants", defineConstants)),
295300
new XElement("ItemGroup",
296301
new XElement("BundleResource",
297302
new XAttribute("Include", Path.GetFullPath(coverageOutputPath)),
@@ -309,6 +314,19 @@ private static XDocument BuildProps(
309314
return new XDocument(project);
310315
}
311316

317+
private static string CreateTargetCompileConstant(string targetId)
318+
{
319+
var builder = new StringBuilder("ENABLE_BINDING_SURFACE_COVERAGE_");
320+
foreach (var character in targetId)
321+
{
322+
builder.Append(char.IsLetterOrDigit(character)
323+
? char.ToUpperInvariant(character)
324+
: '_');
325+
}
326+
327+
return builder.ToString();
328+
}
329+
312330
private static IReadOnlyList<BindingSurfaceCoverageTargetManifest> SelectTargets(
313331
IReadOnlyList<BindingSurfaceCoverageTargetManifest> targets,
314332
string selectedTarget)
@@ -496,7 +514,7 @@ private static IEnumerable<BindingSurfaceDescriptor> BuildSurfaces(
496514
ObjectiveCName: null,
497515
ContainerKind: "manual",
498516
IsProtocol: false,
499-
IsStatic: false,
517+
IsStatic: manualItem.IsStatic,
500518
MemberName: manualItem.MemberName,
501519
BindingAttribute: bindingAttribute,
502520
BindingValue: bindingValue,
@@ -506,7 +524,7 @@ private static IEnumerable<BindingSurfaceDescriptor> BuildSurfaces(
506524
ParameterTypes: manualItem.ParameterTypes,
507525
ReturnType: manualItem.ReturnType,
508526
NativeSelectors: string.Equals(bindingAttribute, "Export", StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(bindingValue)
509-
? [new BindingSurfaceNativeSelector(bindingValue!, IsStatic: false, IsProtocol: false)]
527+
? [new BindingSurfaceNativeSelector(bindingValue!, manualItem.IsStatic, IsProtocol: false)]
510528
: EmptySelectors,
511529
SourceFile: manualItem.SourceFile,
512530
Signature: manualItem.Signature);

tests/E2E/Firebase.Foundation/FirebaseFoundationE2E/FirebaseBindingSurfaceCoverage.AppDistribution.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#if ENABLE_BINDING_SURFACE_COVERAGE
2+
#if ENABLE_BINDING_SURFACE_COVERAGE_APPDISTRIBUTION
23
using FirebaseAppDistributionClient = Firebase.AppDistribution.AppDistribution;
34
using Foundation;
45
using UIKit;
6+
#endif
57

68
namespace FirebaseFoundationE2E;
79

@@ -10,10 +12,13 @@ public static partial class FirebaseBindingSurfaceCoverage
1012
static async Task<BindingSurfaceCoverageTargetResult> VerifyAppDistributionBindingSurfaceAsync(BindingSurfaceCoverageDocument document)
1113
{
1214
var result = await ExecuteTargetAsync(document, "AppDistribution");
15+
#if ENABLE_BINDING_SURFACE_COVERAGE_APPDISTRIBUTION
1316
await ExerciseAppDistributionBoundaryAsync();
17+
#endif
1418
return result;
1519
}
1620

21+
#if ENABLE_BINDING_SURFACE_COVERAGE_APPDISTRIBUTION
1722
static async Task ExerciseAppDistributionBoundaryAsync()
1823
{
1924
var appDistribution = FirebaseAppDistributionClient.SharedInstance
@@ -55,5 +60,6 @@ static async Task ExerciseAppDistributionBoundaryAsync()
5560
_ = await updateCompletion.Task;
5661
}
5762
}
63+
#endif
5864
}
5965
#endif

0 commit comments

Comments
 (0)