Skip to content

Make SetDefault() public on Essentials types for third-party platform backends - #34229

Closed
Redth wants to merge 10 commits into
net11.0from
dev/redth/fix-34100
Closed

Make SetDefault() public on Essentials types for third-party platform backends#34229
Redth wants to merge 10 commits into
net11.0from
dev/redth/fix-34100

Conversation

@Redth

@Redth Redth commented Feb 25, 2026

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description

Fixes #34100

Third-party platform backends (e.g. Linux/GTK) cannot wire their Essentials implementations into the static Default properties (FilePicker.Default, Preferences.Default, SecureStorage.Default, etc.) without using private reflection. The internal SetDefault() method is currently the only way to set these, but it is not accessible to external code.

This PR makes SetDefault() public on all 33 Essentials types that follow the Default/SetDefault pattern, enabling custom backends to register their implementations directly:

// In a third-party platform backend (e.g. Linux/GTK):
Preferences.SetDefault(new LinuxPreferences());
FilePicker.SetDefault(new LinuxFilePicker());
SecureStorage.SetDefault(new LinuxSecureStorage());

Changes

  • Changed SetDefault() from internal to public on all 33 Essentials types:
    • 30 cross-platform types (Preferences, FilePicker, SecureStorage, Clipboard, Launcher, Browser, etc.)
    • 3 platform-specific types (ActivityStateManager on Android, WindowStateManager on iOS/Windows)
  • Added XML documentation to all SetDefault() methods
  • Updated PublicAPI.Unshipped.txt for all 7 TFMs (net, net-android, net-ios, net-maccatalyst, net-windows, net-tizen, netstandard)

Affected Types

All types under Microsoft.Maui.Storage, Microsoft.Maui.ApplicationModel, Microsoft.Maui.Devices, Microsoft.Maui.Media, Microsoft.Maui.Accessibility, and Microsoft.Maui.Authentication that expose a static Default property backed by a SetDefault() method.

…form backends

Fixes #34100

Third-party platform backends (e.g. Linux/GTK) cannot wire their
Essentials implementations into the static Default properties without
using private reflection. This change makes the internal SetDefault()
method public on all 33 Essentials types, enabling custom backends to
register implementations directly:

    Preferences.SetDefault(new LinuxPreferences());
    FilePicker.SetDefault(new LinuxFilePicker());

Changes:
- Changed SetDefault() from internal to public on all Essentials types
- Added XML documentation to all SetDefault() methods
- Updated PublicAPI.Unshipped.txt for all TFMs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 25, 2026 02:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR broadens .NET MAUI Essentials extensibility by making the SetDefault(...) registration hook public across all Essentials APIs that expose a static Default implementation, enabling third-party platform backends (e.g., Linux/GTK) to wire their implementations without reflection.

Changes:

  • Changed SetDefault(...) from internal to public across the Essentials Default/SetDefault pattern types.
  • Added XML documentation to the newly-public SetDefault(...) methods (including null-to-reset behavior).
  • Updated PublicAPI.Unshipped.txt across TFMs to reflect the new public surface area.

Reviewed changes

Copilot reviewed 40 out of 40 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Essentials/src/WebAuthenticator/WebAuthenticator.shared.cs Makes SetDefault(IWebAuthenticator?) public and documents it.
src/Essentials/src/WebAuthenticator/AppleSignInAuthenticator.shared.cs Makes SetDefault(IAppleSignInAuthenticator?) public and documents it.
src/Essentials/src/Vibration/Vibration.shared.cs Makes SetDefault(IVibration?) public and documents it.
src/Essentials/src/VersionTracking/VersionTracking.shared.cs Makes SetDefault(IVersionTracking?) public and documents it.
src/Essentials/src/TextToSpeech/TextToSpeech.shared.cs Makes SetDefault(ITextToSpeech?) public and documents it.
src/Essentials/src/Sms/Sms.shared.cs Makes SetDefault(ISms?) public and documents it.
src/Essentials/src/Share/Share.shared.cs Makes SetDefault(IShare?) public and documents it.
src/Essentials/src/SemanticScreenReader/SemanticScreenReader.shared.cs Makes SetDefault(ISemanticScreenReader?) public and documents it.
src/Essentials/src/SecureStorage/SecureStorage.shared.cs Makes SetDefault(ISecureStorage?) public and documents it.
src/Essentials/src/Screenshot/Screenshot.shared.cs Makes SetDefault(IScreenshot?) public and documents it.
src/Essentials/src/Preferences/Preferences.shared.cs Makes SetDefault(IPreferences?) public and documents it.
src/Essentials/src/Platform/ActivityStateManager.android.cs Makes Android ActivityStateManager.SetDefault(...) public and documents it.
src/Essentials/src/Platform/WindowStateManager.ios.cs Makes iOS WindowStateManager.SetDefault(...) public and documents it.
src/Essentials/src/Platform/windowstateManager.windows.cs Makes Windows WindowStateManager.SetDefault(...) public and documents it.
src/Essentials/src/PhoneDialer/PhoneDialer.shared.cs Makes SetDefault(IPhoneDialer?) public and documents it.
src/Essentials/src/OrientationSensor/OrientationSensor.shared.cs Makes SetDefault(IOrientationSensor?) public and documents it.
src/Essentials/src/MediaPicker/MediaPicker.shared.cs Makes SetDefault(IMediaPicker?) public and documents it.
src/Essentials/src/Map/Map.shared.cs Makes SetDefault(IMap?) public and documents it.
src/Essentials/src/Magnetometer/Magnetometer.shared.cs Makes SetDefault(IMagnetometer?) public and documents it.
src/Essentials/src/Launcher/Launcher.shared.cs Makes SetDefault(ILauncher?) public and documents it.
src/Essentials/src/HapticFeedback/HapticFeedback.shared.cs Makes SetDefault(IHapticFeedback?) public and documents it.
src/Essentials/src/Gyroscope/Gyroscope.shared.cs Makes SetDefault(IGyroscope?) public and documents it.
src/Essentials/src/Geolocation/Geolocation.shared.cs Makes SetDefault(IGeolocation?) public and documents it.
src/Essentials/src/Flashlight/Flashlight.shared.cs Makes SetDefault(IFlashlight?) public and documents it.
src/Essentials/src/FilePicker/FilePicker.shared.cs Makes SetDefault(IFilePicker?) public and documents it.
src/Essentials/src/Email/Email.shared.cs Makes SetDefault(IEmail?) public and documents it.
src/Essentials/src/Contacts/Contacts.shared.cs Makes SetDefault(IContacts?) public and documents it.
src/Essentials/src/Compass/Compass.shared.cs Makes SetDefault(ICompass?) public and documents it.
src/Essentials/src/Clipboard/Clipboard.shared.cs Makes SetDefault(IClipboard?) public and documents it.
src/Essentials/src/Browser/Browser.shared.cs Makes SetDefault(IBrowser?) public and documents it.
src/Essentials/src/Battery/Battery.shared.cs Makes SetDefault(IBattery?) public and documents it.
src/Essentials/src/Barometer/Barometer.shared.cs Makes SetDefault(IBarometer?) public and documents it.
src/Essentials/src/Accelerometer/Accelerometer.shared.cs Makes SetDefault(IAccelerometer?) public and documents it.
src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt Adds public API entries for the newly-public SetDefault(...) methods (netstandard).
src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt Adds public API entries for the newly-public SetDefault(...) methods (net).
src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt Adds public API entries including Windows-specific WindowStateManager.SetDefault(...).
src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Adds public API entries for the newly-public SetDefault(...) methods (Tizen).
src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Adds public API entries including WindowStateManager.SetDefault(...) (MacCatalyst).
src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Adds public API entries including WindowStateManager.SetDefault(...) (iOS).
src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Adds public API entries including Android-specific ActivityStateManager.SetDefault(...).

@MauiBot

MauiBot commented Mar 23, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI Summary

📊 Expand Full Reviewe9162bc · Make SetDefault() public on all Essentials types for third-party platform backends
🔍 Pre-Flight — Context & Validation

Issue: #34100 - Essentials static Default properties need a public registration mechanism - Every Essentials API (Preferences, FilePicker, SecureStorage, etc.) is inaccessible to custom backends without reflection hacks
PR: #34229 - Make SetDefault() public on Essentials types for third-party platform backends
Platforms Affected: Cross-platform Essentials APIs for third-party backends; platform-specific hooks added for Android, iOS/MacCatalyst, and Windows
Files Changed: 33 implementation, 0 test, 7 PublicAPI

Key Findings

  • The linked issue requests a public registration mechanism so third-party backends can replace Essentials Default implementations without private reflection.
  • The PR description explicitly chooses the minimal Option A from the issue: make SetDefault() public on every Essentials type that follows the Default/SetDefault pattern.
  • There are no PR issue comments, no inline review comments, and no prior agent-review summary to import.
  • The file set contains implementation/API-surface changes only: 33 source files and 7 PublicAPI.Unshipped.txt files, with no new or modified tests.
  • Android is selected for gate/verification because the PR includes an Android-specific API surface change (ActivityStateManager.android.cs) and the requested test platform is android.

Edge Cases Noted

  • The issue mentions an alternative DI-based registration path, but this PR does not implement that broader design.
  • The issue scope is broader than Android because many changed SetDefault() methods are cross-platform, while three are platform-specific.
  • Because the PR adds public API across 7 TFMs, PublicAPI correctness is part of the change surface even without dedicated runtime tests.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34229 Make SetDefault(...) public on all Essentials types using the static Default/SetDefault pattern, add XML docs, and update PublicAPI files across TFMs ⏳ PENDING (Gate) 40 files Original PR

🚦 Gate — Test Verification

Gate Result: ⚠️ SKIPPED

Platform: android
Mode: Full Verification

  • Tests FAIL without fix: ❌
  • Tests PASS with fix: ❌

Reason: PR #34229 does not add or modify any test files. The changed file set is 33 implementation files plus 7 PublicAPI.Unshipped.txt files, so there is no PR-supplied test to run through fail-without-fix / pass-with-fix verification.

Recommendation from Gate: Add targeted coverage (likely Essentials unit tests) to prove an external caller can replace Default via the newly-public SetDefault(...) methods.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Add public Geocoding.SetDefault(...), keep internal SetCurrent(...), and add focused Geocoding unit tests ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, 7 PublicAPI files Additive, backward-compatible fix for the omitted API
2 try-fix (claude-sonnet-4.6) Rename Geocoding.SetCurrent(...) directly to public SetDefault(...) and add focused Geocoding unit tests ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, 7 PublicAPI files Cleanest naming consistency; no internal callers of SetCurrent remain
3 try-fix (gpt-5.3-codex) Add public SetDefault(...) and expose public SetCurrent(...) as a forwarding compatibility alias ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, 7 PublicAPI files Works, but broadens public API more than the issue requires
4 try-fix (gemini-3-pro-preview) Make Geocoding.Default publicly settable and add focused unit tests ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, 7 PublicAPI files Functional, but changes the public shape more than other Essentials APIs
5 try-fix (gpt-5.3-codex round 2) Add public ConfigureDefault(Func<IGeocoding?>?) resolver hook with lazy caching, plus focused tests ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, 7 PublicAPI files Materially different, but introduces a one-off API pattern unlike the rest of Essentials
6 try-fix (claude-opus-4.6 round 2) Add public SetServiceProvider(IServiceProvider?) so Default resolves IGeocoding from DI first ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, 7 PublicAPI files Architecturally distinct, but much broader than the PR and not aligned with other Essentials APIs
7 try-fix (gemini-3-pro-preview round 2) Add ConfigureDefault(...) in Essentials plus a MauiAppBuilder.ConfigureGeocoding(...) integration path in Core ✅ PASS Geocoding.shared.cs, Geocoding_Tests.cs, Core hosting/API files, PublicAPI files Works, but expands a one-file PR gap into a multi-assembly design change
PR PR #34229 Make existing SetDefault(...) methods public on 33 Essentials types and update docs/PublicAPI ⚠️ SKIPPED (Gate) 40 files No PR-supplied tests to verify fail-without-fix / pass-with-fix; still misses Geocoding

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 Yes Add a public SetDefault(...) wrapper specifically for Geocoding and validate with targeted unit tests.
claude-sonnet-4.6 1 Yes Replace SetCurrent(...) with public SetDefault(...) for Geocoding and validate with targeted unit tests.
gpt-5.3-codex 1 Yes Expose both public names, with SetCurrent(...) forwarding to SetDefault(...), and validate with targeted unit tests.
gemini-3-pro-preview 1 Yes Make Geocoding.Default settable instead of adding another method.
claude-opus-4.6 2 Yes Resolve IGeocoding from DI/services via public SetServiceProvider(IServiceProvider?).
claude-sonnet-4.6 2 No NO NEW IDEAS
gpt-5.3-codex 2 Yes Add a public resolver/factory hook ConfigureDefault(Func<IGeocoding?>?) with lazy caching.
gemini-3-pro-preview 2 Yes Add a MauiAppBuilder.ConfigureGeocoding(IGeocoding) extension method instead of changing Geocoding directly.
claude-opus-4.6 3 No NO NEW IDEAS
claude-sonnet-4.6 3 Yes Generic cross-Essentials registry idea — broader than this PR and not attempted because round limit reached.
gpt-5.3-codex 3 Yes Implicit DI via IPlatformApplication.Current.Services — broader than this PR and not attempted because round limit reached.
gemini-3-pro-preview 3 Yes Event-based pull registration — broader than this PR and not attempted because round limit reached.

Exhausted: Yes — final allowed cross-pollination round reached
Selected Fix: Candidate #2 — smallest fix that fully addresses the missed API while matching the exact pattern already used by the other Essentials types in this PR


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Linked issue #34100, file classification, and PR discussion gathered.
Gate ⚠️ SKIPPED PR adds no tests, so fail-without-fix / pass-with-fix verification could not run.
Try-Fix ✅ COMPLETE 7 attempts, 7 passing candidates; best candidate is not the PR as written.
Report ✅ COMPLETE

Summary

PR #34229 correctly publicizes SetDefault(...) on 33 Essentials types, but it leaves one relevant API behind: Geocoding. Geocoding still exposes public static IGeocoding Default while only providing internal static void SetCurrent(IGeocoding? implementation), so third-party backends still cannot register a custom Geocoding implementation without reflection.

Because of that omission, the PR does not fully satisfy issue #34100. It also ships without targeted tests proving the new public registration hooks work.

Root Cause

The PR appears to have enumerated only the existing Default/SetDefault pattern and missed the one inconsistent outlier: src/Essentials/src/Geocoding/Geocoding.shared.cs, which uses Default plus SetCurrent(...) instead. That naming inconsistency caused Geocoding to be excluded from the mechanical update, leaving the issue only partially solved.

Fix Quality

The existing PR changes are mechanically consistent for the 33 touched APIs, and the PublicAPI updates for those APIs look correct. The problem is completeness: the same user-facing extensibility hole remains for Geocoding.

The strongest alternative from Try-Fix was candidate #2: rename Geocoding.SetCurrent(...) directly to public SetDefault(...), add XML documentation, update the 7 PublicAPI.Unshipped.txt files, and add focused Geocoding_Tests coverage. That option passed targeted unit tests, matches the exact pattern already used by the other Essentials types in this PR, and is smaller/cleaner than the broader DI or builder-based variants.


@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Mar 23, 2026
@github-actions

github-actions Bot commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34229

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34229"

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 42 out of 42 changed files in this pull request and generated 3 comments.

Comment thread src/Essentials/test/UnitTests/SetDefault_Tests.cs
Comment thread src/Essentials/test/UnitTests/SetDefault_Tests.cs
Comment thread src/Essentials/test/UnitTests/SetDefault_Tests.cs
PureWeen and others added 3 commits April 2, 2026 16:50
…#34265)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary

Adds a standalone `code-review` skill for reviewing PR code changes with
MAUI-specific domain knowledge, modeled after [dotnet/android's
android-reviewer
skill](https://github.com/dotnet/android/tree/main/.github/skills/android-reviewer).

### What's included

**`.github/skills/code-review/SKILL.md`** (196 lines) — Review workflow:
- Independence-first methodology (read code before PR description to
avoid anchoring bias)
- 6-step workflow: gather context → load rules → independent assessment
→ reconcile with PR narrative → check CI → devil's advocate verdict
- Three verdicts: `LGTM`, `NEEDS_CHANGES`, `NEEDS_DISCUSSION`
- Optional multi-model review for diverse perspectives
- Output constraints: don't pile on, don't flag what CI catches

**`.github/skills/code-review/references/review-rules.md`** (345 lines)
— Domain knowledge:
- **22 sections** of review rules distilled from real FTE code reviews
- **138 PR citations** linking each rule to the PR where the pattern was
identified
- Sourced from **366 PRs**: top 142 most-discussed PRs (2,883 review
comments), 30 reverted PRs, 50 candidate-branch failures, 64 regression
fixes
- Covers: handler lifecycle, memory leaks, safe area, layout, platform
code, Android/iOS/Windows specifics, navigation, CollectionView,
threading, XAML, testing, performance, error handling, API design,
images, gestures, build, accessibility
- §21 Regression Prevention — lessons from reverts and candidate
failures
- §22 Component Risk Table — ranking the most regression-prone
components

### Design decisions

- **Separation of concerns**: SKILL.md = workflow, review-rules.md =
knowledge (follows Android's pattern)
- **Standalone**: Can be invoked directly by users or by other agents.
No coupling to the PR agent pipeline.
- **Data-driven**: Every rule traces to a real PR where a senior
maintainer flagged the pattern. No generic advice.

### Changes to copilot-instructions.md

- Added "Opening PRs" section with required NOTE block template
- Added code-review skill to the skills listing
- Minor reformatting of existing documentation sections

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
- Disable parallel execution for SetDefault_Tests via a dedicated xUnit collection
  to avoid shared static Default implementation races across tests.
- Remove unused System.Threading using.
- Fix MockFilePicker.PickMultipleAsync signature nullability to match IFilePicker.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Redth

Redth commented Apr 3, 2026

Copy link
Copy Markdown
Member Author

Addressed today’s new Copilot suggestion by adding a guard-rail reflection test.

✅ Added

  • src/Essentials/test/UnitTests/SetDefault_Tests.cs
    • New test: AllEssentialsTypes_ExposePublicSetDefault()
    • It reflects over the Essentials assembly and finds all static types under Microsoft.Maui.* namespaces with a public static interface-typed Default property.
    • For each such type, it asserts a matching public static void SetDefault(<interface>) method exists.
    • Fails with a concrete missing-method list if any future API misses this pattern.

✅ Validation run

  • dotnet build src/Essentials/test/UnitTests/Essentials.UnitTests.csproj --no-restore -v q
  • dotnet test src/Essentials/test/UnitTests/Essentials.UnitTests.csproj --no-build --filter "FullyQualifiedName~SetDefault_Tests"
    • Passed: 8, Failed: 0

✅ Push

  • Commit: 748eaa0265
  • Branch: dev/redth/fix-34100

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 42 out of 42 changed files in this pull request and generated 1 comment.

Comment thread src/Essentials/test/UnitTests/SetDefault_Tests.cs
- Add AllEssentialsTypes_ExposePublicSetDefault test to ensure every
  static Essentials type with a public interface-typed Default property
  also exposes a matching public static void SetDefault(<interface>) method.
- Uses reflection over the Essentials assembly and fails with a specific
  missing method list to prevent future accidental API pattern regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

…rloads

 34 types coverage)
- Add double-set test verifying second SetDefault call overrides first
- Fix MockPreferences: remove superfluous single-param overloads not on IPreferences interface
- Add mocks: MockClipboard, MockSecureStorage, MockBattery with delegation verification

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

… type categories

- Add null-reset tests for Clipboard, SecureStorage, Battery
- Add IsUsed behavioral tests for Preferences and FilePicker
- Add full test coverage for Share, Launcher, Accelerometer (sensor/media/app types)
- All 9 tested types now have consistent 3-test pattern: set, null-reset, is-used

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Evaluation

Overall Verdict: ⚠️ Tests need improvement

The unit tests are well-structured and use the right test type, but behavioral coverage exists for only 9 of 31 changed API types. The reflection guardrail catches API surface issues, but doesn't verify the plumbing actually works for the remaining 22 types.

👍 / 👎 — Was this evaluation helpful? React to let us know!

📊 Expand Full Evaluation

PR Test Evaluation Report

PR: #34229 — Essentials SetDefault API visibility fix (internal → public)
Test files evaluated: 1 (SetDefault_Tests.cs)
Fix files: 34 (31 Essentials source files changing internal static void SetDefaultpublic static void SetDefault, plus PublicAPI.Unshipped.txt updates)


Overall Verdict

⚠️ Tests need improvement

The test file is well-designed with a smart reflection guardrail and the right test type (unit tests). However, behavioral tests cover only 9 of the 31 types that gained a public SetDefault method, leaving 22 types verified only at the API-surface level.


1. Fix Coverage — ⚠️

The fix changes SetDefault from internal to public on 31 Essentials types. Tests cover this in two complementary ways:

  • Reflection guardrail (AllEssentialsTypes_ExposePublicSetDefault): Scans the Essentials assembly and asserts every type with a Default property also exposes a public SetDefault. This directly detects a regression if any type's visibility were reverted.
  • ⚠️ Behavioral tests: Only 9 of 31 types are tested with the set/null-reset/is-used pattern (Geocoding, Preferences, FilePicker, Clipboard, SecureStorage, Battery, Share, Launcher, Accelerometer). The remaining 22 types (Barometer, Browser, Compass, Contacts, Email, Flashlight, Geolocation, Gyroscope, HapticFeedback, Magnetometer, Map, MediaPicker, OrientationSensor, PhoneDialer, Screenshot, SemanticScreenReader, Sms, TextToSpeech, VersionTracking, Vibration, WebAuthenticator, AppleSignInAuthenticator) have no behavioral test verifying their plumbing works end-to-end.

The reflection test would catch a missing public API, but not a broken implementation (e.g., if defaultImplementation field was typed incorrectly or the null-reset path silently failed for a specific type).


2. Edge Cases & Gaps — ⚠️

Covered:

  • Set custom implementation and verify Default returns it (9 types)
  • Null-reset and verify Default no longer returns the mock (most tested types)
  • Mock is actually invoked after SetDefault — behavioral "is-used" tests (most tested types)
  • Double-set override (Geocoding only)
  • API surface exhaustiveness via reflection

Missing:

  • 22 types have no behavioral test — while the pattern is identical, types like WebAuthenticator, MediaPicker, TextToSpeech, VersionTracking, and sensor types (Barometer, Compass, Gyroscope, Magnetometer, OrientationSensor) are entirely unverified at the behavioral level. If any of these had a subtle wiring bug (wrong field, wrong null check), the tests would not catch it.
  • Double-set test only for Geocoding — only one type has a test verifying that calling SetDefault twice replaces the first mock with the second. This is a common regression pattern.
  • No test for AppleSignInAuthenticator — this type's SetDefault was also made public and it's a distinct pattern (lives in WebAuthenticator/ namespace with Apple-specific logic).

3. Test Type Appropriateness — ✅

Current: Unit Tests (xUnit [Fact])
Recommendation: Same — unit tests are exactly right here. The fix is a pure API-visibility and plumbing change; no platform context, UI, or native rendering is required to verify it. Using UI or device tests would be unnecessarily heavy.


4. Convention Compliance — ✅

  • [Fact] attributes on all 29 test methods (xUnit convention)
  • [Collection("SetDefault")] with DisableParallelization = true — correct for tests modifying static state
  • try/finally blocks to reset state after each test — good practice
  • ✅ No convention issues detected by automated script

5. Flakiness Risk — ✅ Low

  • [Collection("SetDefault")] with DisableParallelization = true prevents race conditions on shared static state ✅
  • try/finally blocks guarantee SetDefault(null) is called even if assertions fail ✅
  • No Task.Delay or Thread.Sleep
  • No UI or Appium dependencies ✅

6. Duplicate Coverage — ✅ No duplicates

No similar SetDefault behavioral tests exist in the codebase. Existing tests in Geocoding_Tests.cs, Browser_Tests.cs, and OrientationSensor_Tests.cs test the platform behavior of those APIs, not the SetDefault plumbing — no overlap.


7. Platform Scope — ⚠️

The fix touches all platforms (Android, iOS, MacCatalyst, Windows, cross-platform). Unit tests run in a netstandard/net TFM context without platform-specific code paths. Since the change is purely API visibility (not platform behavior), unit tests are appropriate and platform-specific device tests are not needed. However, if CI runs these unit tests only on one platform, it's worth confirming they run on at least the net and netstandard TFMs.


8. Assertion Quality — ✅

  • Assert.Same(custom, Type.Default) — identity check, not equality; correctly verifies the exact mock instance ✅
  • Assert.NotSame(custom, Type.Default) — verifies null-reset restores a different (platform default) instance ✅
  • Assert.True(custom.SomeCalled) — verifies the mock was actually invoked via the static API ✅
  • No magic numbers or over-broad assertions ✅

9. Fix-Test Alignment — ✅

The test file directly targets the SetDefault mechanism in Essentials types, which is exactly what the fix changes. The reflection test is particularly well-aligned — it enforces the invariant that every type with Default must also have a public SetDefault, making it a regression guard for the exact change being made.


Recommendations

  1. Add behavioral tests for the remaining 22 types — at minimum, the most commonly used ones: Browser, Sms, Email, Contacts, TextToSpeech, Vibration, HapticFeedback, Flashlight, Screenshot, Geolocation. Consider a [Theory] or a helper method to reduce boilerplate, since all types follow the same 3-test pattern.

  2. Add a double-set test for at least one additional type — e.g., Preferences or Battery, to verify the second assignment fully replaces the first independent of type-specific implementation details.

  3. Add a test for AppleSignInAuthenticator.SetDefault — this is a less common type with Apple-specific concerns; a basic set/null-reset test would confirm it follows the same pattern.

Note

🔒 Integrity filtering filtered 1 item

Integrity filtering activated and filtered the following item during workflow execution.
This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.

🧪 Test evaluation by Evaluate PR Tests

@PureWeen

Copy link
Copy Markdown
Member

Multi-Model Code Review — PR #34229

PR: Make SetDefault() public on Essentials types for third-party platform backends
Author: @Redth | Target: main | Fixes: #34100
CI: ✅ All 35 checks passing | Review method: 3 independent reviewers with adversarial consensus


🟡 MODERATE — Target branch should be net10.0, not main

Flagged by: All 3 reviewers | Consensus: Unanimous

Per the contribution guidelines, main is for bug fixes without API changes, and net10.0 is for new features and API changes. This PR adds 33+ new public API methods (confirmed by the PublicAPI.Unshipped.txt additions across all 7 TFMs). Changing internalpublic is definitionally new API surface. The linked issue (#34100) does not indicate this is a regression requiring main.

Ask: Confirm with maintainers whether this is an approved servicing exception, or retarget to net10.0.


🟡 MODERATE — Thread safety: defaultImplementation has no synchronization

Flagged by: All 3 reviewers | Consensus: Unanimous

All 33 types use a plain field write in SetDefault:

public static void SetDefault(IFoo? implementation) => defaultImplementation = implementation;

And a ??= lazy init on the getter:

public static IFoo Default => defaultImplementation ??= new FooImplementation();

The ??= operator is not atomic — two threads can both observe null, both instantiate, and one instance is silently discarded. More critically, SetDefault(null) racing with a Default read is a data race on a non-volatile field. While this was already true when internal, promoting to public exposes it to third-party code with no controlled call sites.

Ask: At minimum, document that SetDefault() must be called during app initialization before any Default access. Alternatively, consider Volatile.Write/Volatile.Read or Interlocked.CompareExchange.

Files: All 33 *.shared.cs files (e.g., Accelerometer.shared.cs:99-104)


🟢 MINOR — Reflection guardrail test has blind spots

Flagged by: 2/3 reviewers | Consensus: Majority

Platform-specific types invisible: AllEssentialsTypes_ExposePublicSetDefault() runs as a unit test under a non-platform TFM. ActivityStateManager (Android-only), WindowStateManager (iOS/Windows-only) will not be in the loaded assembly. If someone accidentally reverts one of these to internal, the guardrail won't catch it.

Current-pattern types invisible: The test scans for properties named Default. The 6 excluded types (AppActions, AppInfo, Connectivity, DeviceDisplay, DeviceInfo, FileSystem) use a Current property instead and are silently invisible to the guardrail — future maintainers won't know they're excluded.

Ask: Add a comment in the test documenting the known blind spots, or expand the test to assert an expected count of discovered types.

File: src/Essentials/test/UnitTests/SetDefault_Tests.cs:30-55


🟢 MINOR — 6 Current/SetCurrent types excluded with no follow-up

Flagged by: All 3 reviewers | Consensus: Unanimous

AppActions, AppInfo, Connectivity, DeviceDisplay, DeviceInfo, and FileSystem use the Current/SetCurrent pattern and remain internal. For the stated use case (Linux/GTK backend), Connectivity, DeviceInfo, and FileSystem are among the most commonly-used Essentials APIs. A third-party backend author would discover they can replace Preferences and FilePicker but not Connectivity or DeviceInfo.

Ask: File a follow-up issue tracking these types, or document in the PR description why they're intentionally excluded.


🟢 MINOR — Behavioral test coverage is sparse (9/33 types)

Flagged by: All 3 reviewers | Consensus: Unanimous

The reflection guardrail verifies all types expose a public SetDefault(), but only 9 types have behavioral tests proving the custom implementation is actually invoked via the static facade. The remaining 24 types rely on structural similarity. This is a pragmatic tradeoff given the mechanical consistency of the pattern, but edge cases (e.g., types that cache the implementation differently) could slip through.

File: src/Essentials/test/UnitTests/SetDefault_Tests.cs


✅ No Issues Found

  • Geocoding rename (SetCurrentSetDefault): Safe — verified zero production callers. Correctly aligns method name with the Default property. (All 3 reviewers agree)
  • Security: No implications — these are service locator setters for platform abstractions, not privilege-granting APIs
  • Data loss: Not applicable
  • PublicAPI files: Correctly updated for all 7 TFMs with platform-specific entries in the right files
  • XML doc comments: Consistent and accurate across all 33 types
  • Mechanical correctness: Every change follows the identical internalpublic + doc comment pattern with no logic changes

Discarded Finding (No Consensus)

One reviewer flagged that AppleSignInAuthenticator.SetDefault being public on non-Apple TFMs might mislead library authors. After adversarial review, this was contested — Apple Sign-In can be implemented cross-platform via server-side OAuth, so the extensibility point is legitimate on all platforms. Finding discarded (split decision).


Prior Review Comments

  • copilot-pull-request-reviewer: Posted 3 review comments (style/minor) — no overlap with this review
  • github-actions (test evaluation): Repeatedly flagged behavioral test coverage gaps — aligns with our 🟢 MINOR finding above
  • @Redth: Added reflection guardrail test in response to earlier feedback ✅

Recommended Action

⚠️ Request changes — Two items need resolution before merge:

  1. Target branch: Confirm with maintainers this is an approved exception for main, or retarget to net10.0
  2. Thread safety: Add documentation (at minimum) that SetDefault() is startup-only, or add synchronization

The remaining 🟢 MINOR findings are low-risk and could be addressed in follow-up work.

@Redth

Redth commented Apr 21, 2026

Copy link
Copy Markdown
Member Author

Ignoring the target-branch discussion, here's my take on the remaining points:

  1. Thread-safety on defaultImplementation — fair observation, but not something introduced by this PR. The same lazy-init/setter pattern already exists across Essentials, including the Current/SetCurrent APIs. These hooks are intended for startup-time registration, not runtime mutation from arbitrary threads, so I don't see this as a blocker for making the existing registration surface public.

  2. Reflection guardrail blind spotsagreed, but minor. The guardrail is specifically validating the Default/SetDefault pattern changed here, so platform-only and Current-pattern APIs are outside its scope. That's worth documenting or tightening later, but it doesn't undermine the correctness of this PR.
    Added more tests.

  3. Current/SetCurrent types still excludedalso fair as follow-up scope, but I don't think it should expand this PR. The issue being fixed here is about the Default-based Essentials types; Connectivity, AppInfo, DeviceInfo, FileSystem, etc. should be tracked separately if we want to expose those as well.
    These are now public too.

  4. Behavioral coveragethis was a reasonable concern. The reflection test was a good regression guard for API surface, but broader plumbing coverage is the right follow-up to make sure the custom implementation path is exercised more systematically.
    Added more tests.

@PureWeen ^

@Redth
Redth changed the base branch from main to net11.0 April 21, 2026 20:31
@Redth

Redth commented Apr 21, 2026

Copy link
Copy Markdown
Member Author

Closing in favour of #35068 after some discussion about moving more towards obsoleting the Default static accessors for these API's.

@Redth Redth closed this Apr 21, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators May 22, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

4 participants