Skip to content

Commit aef131c

Browse files
committed
Metro: last three sync plugins registered from a container, Dagger keeps ownership
1 parent 352cceb commit aef131c

5 files changed

Lines changed: 111 additions & 65 deletions

File tree

app/src/main/kotlin/app/aaps/di/CoreObjectsModule.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ import app.aaps.core.objects.wizard.BolusWizard
116116
import app.aaps.core.objects.wizard.QuickWizard
117117
import app.aaps.core.ui.search.SearchableProvider
118118
import app.aaps.core.utils.receivers.DataInbox
119+
import app.aaps.plugins.sync.wear.WearPlugin
120+
import app.aaps.plugins.sync.nsclientV3.NSClientV3Plugin
121+
import app.aaps.plugins.sync.smsCommunicator.SmsCommunicatorPlugin
119122
import app.aaps.implementation.plugin.PluginStore
120123
import app.aaps.di.metro.AapsLeaves
121124
import app.aaps.di.metro.MetroGraphs
@@ -454,6 +457,9 @@ class CoreObjectsModule {
454457
versionCheckerUtilsProvider: Provider<VersionCheckerUtils>,
455458
searchableProvidersProvider: Provider<Set<SearchableProvider>>,
456459
permissionProvidersProvider: Provider<Set<PermissionProvider>>,
460+
smsCommunicatorPluginProvider: Provider<SmsCommunicatorPlugin>,
461+
nsClientV3PluginProvider: Provider<NSClientV3Plugin>,
462+
wearPluginProvider: Provider<WearPlugin>,
457463
bolusProgressDataProvider: Provider<BolusProgressData>,
458464
pumpEnactResultProvider: Provider<PumpEnactResult>,
459465
historyScopeProvider: Provider<HistoryScope>,
@@ -492,6 +498,9 @@ class CoreObjectsModule {
492498
versionCheckerUtilsProvider,
493499
searchableProvidersProvider,
494500
permissionProvidersProvider,
501+
smsCommunicatorPluginProvider,
502+
nsClientV3PluginProvider,
503+
wearPluginProvider,
495504
bolusProgressDataProvider,
496505
pumpEnactResultProvider,
497506
historyScopeProvider,

app/src/main/kotlin/app/aaps/di/metro/AapsLeaves.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ import app.aaps.core.keys.interfaces.Preferences
4848
import app.aaps.core.nssdk.interfaces.RunningConfiguration
4949
import app.aaps.core.ui.search.SearchableProvider
5050
import app.aaps.core.utils.receivers.DataInbox
51+
import app.aaps.plugins.sync.wear.WearPlugin
52+
import app.aaps.plugins.sync.nsclientV3.NSClientV3Plugin
53+
import app.aaps.plugins.sync.smsCommunicator.SmsCommunicatorPlugin
5154
import app.aaps.implementation.maintenance.cloud.CloudStorageManager
5255
import app.aaps.implementation.scenes.SceneExecutor
5356
import app.aaps.plugins.automation.services.LastLocationDataContainer
@@ -136,6 +139,9 @@ class AapsLeaves(
136139
// binding rather than re-declaring the multibinding on this side.
137140
private val searchableProvidersProvider: Provider<Set<SearchableProvider>>,
138141
private val permissionProvidersProvider: Provider<Set<PermissionProvider>>,
142+
private val smsCommunicatorPluginProvider: Provider<SmsCommunicatorPlugin>,
143+
private val nsClientV3PluginProvider: Provider<NSClientV3Plugin>,
144+
private val wearPluginProvider: Provider<WearPlugin>,
139145
/**
140146
* The history browser scope.
141147
*
@@ -222,6 +228,13 @@ class AapsLeaves(
222228
// LocationServiceController, ReminderScheduler and BtConnectionSource, none of which the graph
223229
// reaches yet. PluginStore takes it through a lambda, so nothing is built until it is asked for.
224230
@Provides fun permissionProviders(): Set<PermissionProvider> = permissionProvidersProvider.get()
231+
232+
// Dagger owns these three, the same way it owns the pump drivers: AuthRequest, the nine
233+
// @HiltWorker loaders under nsclientV3 and the wear data layer all inject the concrete class, so
234+
// Dagger builds them and Metro borrows. See SyncPluginsBindings for how they reach the plugin map.
235+
@Provides fun smsCommunicatorPlugin(): SmsCommunicatorPlugin = smsCommunicatorPluginProvider.get()
236+
@Provides fun nsClientV3Plugin(): NSClientV3Plugin = nsClientV3PluginProvider.get()
237+
@Provides fun wearPlugin(): WearPlugin = wearPluginProvider.get()
225238
@Provides fun overviewDataCache(): OverviewDataCache = overviewDataCacheProvider.get()
226239

227240
/** Hilt's qualifier, read now that interop is on. Same Context as the unqualified binding. */
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package app.aaps.di.metro
2+
3+
import app.aaps.core.interfaces.di.NotNSClient
4+
import app.aaps.core.interfaces.plugin.PluginBase
5+
import app.aaps.plugins.sync.nsclientV3.NSClientV3Plugin
6+
import app.aaps.plugins.sync.smsCommunicator.SmsCommunicatorPlugin
7+
import app.aaps.plugins.sync.wear.WearPlugin
8+
import dev.zacsweers.metro.AppScope
9+
import dev.zacsweers.metro.BindingContainer
10+
import dev.zacsweers.metro.ContributesTo
11+
import dev.zacsweers.metro.IntKey
12+
import dev.zacsweers.metro.IntoMap
13+
import dev.zacsweers.metro.Provides
14+
import dev.zacsweers.metro.SingleIn
15+
16+
/**
17+
* The last three `:plugins:sync` plugins, registered from here rather than from the class.
18+
*
19+
* ## Why not annotate the class, like the other four
20+
*
21+
* Dagger's KSP `InjectProcessingStep` runs over every class it has to construct, reads the annotations
22+
* on it, and cannot resolve `dev.zacsweers.metro.IntKey`:
23+
*
24+
* ```
25+
* InjectProcessingStep was unable to process 'NSClientV3Plugin(...)'
26+
* => annotation: @IntKey => type (ERROR annotation type): error.NonExistentClass
27+
* ```
28+
*
29+
* Tidepool, Xdrip, Tizen and Garmin have no Dagger-built consumer, so Dagger never processes them and
30+
* they carry their own `@ContributesIntoMap`. These three do: `AuthRequest` injects
31+
* [SmsCommunicatorPlugin], the nine `@HiltWorker` loaders inject [NSClientV3Plugin], and the wear data
32+
* layer injects [WearPlugin]. Registering the entry here keeps the Metro annotations off the class.
33+
*
34+
* ## Who owns the instance
35+
*
36+
* **Dagger**, for the same reason it owns the pump drivers: the classes above are Dagger-built, so
37+
* Dagger's copy is the live one. These take the plugin from `AapsLeaves`, which hands Dagger's singleton
38+
* over, so the object in the plugin map is the object those consumers use. Giving Metro its own would be
39+
* the split-brain that `PumpLeaves` documents - a plugin in the list that nothing ever writes to.
40+
*
41+
* `@SingleIn` here does not compete with that: it makes Metro hold **one reference to Dagger's object**
42+
* rather than calling the leaf on every read. Without it `ContributedPluginsTest` fails with "plugin 350
43+
* is rebuilt on every read", because a plugin has to be one object for its enabled state to mean
44+
* anything.
45+
*/
46+
@ContributesTo(AppScope::class)
47+
@BindingContainer
48+
object SyncPluginsBindings {
49+
50+
@Provides
51+
@SingleIn(AppScope::class)
52+
@IntoMap
53+
@NotNSClient
54+
@IntKey(300)
55+
fun smsCommunicatorPlugin(plugin: SmsCommunicatorPlugin): PluginBase = plugin
56+
57+
@Provides
58+
@SingleIn(AppScope::class)
59+
@IntoMap
60+
@IntKey(310)
61+
fun nsClientV3Plugin(plugin: NSClientV3Plugin): PluginBase = plugin
62+
63+
@Provides
64+
@SingleIn(AppScope::class)
65+
@IntoMap
66+
@IntKey(350)
67+
fun wearPlugin(plugin: WearPlugin): PluginBase = plugin
68+
}

app/src/test/kotlin/app/aaps/di/metro/ContributedPluginsTest.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import app.aaps.plugins.smoothing.UnscentedKalmanFilterPlugin
1212
import com.google.common.truth.Truth.assertThat
1313
import com.google.common.truth.Truth.assertWithMessage
1414
import org.junit.jupiter.api.Test
15+
import org.mockito.Mockito.mockingDetails
1516

1617
/**
1718
* Plugins that register themselves with `@ContributesIntoMap` really reach the root graph.
@@ -42,9 +43,9 @@ class ContributedPluginsTest {
4243
// source - all sixteen. 400, 410 and 440 are also bound to an interface; Dagger delegates
4344
// to these instances in CoreObjectsModule rather than building its own.
4445
400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550,
45-
// sync - the every-build part of the 300 block. SmsCommunicator 300 and NSClientV3 310 are
46-
// still Dagger, and Tidepool 320 is @NotNSClient, so only these three are here.
47-
330, 360, 370,
46+
// sync - the every-build part of the 300 block. SmsCommunicator 300 and Tidepool 320 are
47+
// @NotNSClient; 340 (OpenHumans) comes from its own graph extension.
48+
310, 330, 350, 360, 370,
4849
// smoothing
4950
600, 610, 620, 630,
5051
// calibration
@@ -68,6 +69,22 @@ class ContributedPluginsTest {
6869
.containsExactly(1010, 1020, 1030, 1040, 1050, 1060, 1080, 1090, 1100, 1120, 1130)
6970
}
7071

72+
@Test
73+
fun `the three Dagger-owned sync plugins are borrowed, not rebuilt by Metro`() {
74+
// Same rule as the pump drivers, and the same check: testRoot() mocks AapsLeaves, so a plugin
75+
// handed over by a leaf comes back a mock while one Metro constructed comes back real. These
76+
// three must stay Dagger's, because AuthRequest, the nine @HiltWorker loaders and the wear data
77+
// layer all inject the concrete class - a second copy would sit in the plugin list unused.
78+
val root = testRoot()
79+
val borrowed = listOf(
80+
300 to root.contributedNotNsClientPlugins[300],
81+
310 to root.contributedPlugins[310],
82+
350 to root.contributedPlugins[350]
83+
)
84+
85+
assertThat(borrowed.filter { !mockingDetails(it.second).isMock }).isEmpty()
86+
}
87+
7188
@Test
7289
fun `the xdrip plugin and the XDripBroadcast binding are the same object`() {
7390
// XdripPlugin carries two contributions: into the plugin map, and as XDripBroadcast. Under
@@ -108,7 +125,7 @@ class ContributedPluginsTest {
108125
fun `only a non-follower build gets the version checker and Tidepool`() {
109126
// 340 (OpenHumans) is also @NotNSClient but comes from its own graph extension, so it joins
110127
// this bucket in MetroGraphs.notNsClientPlugins() rather than appearing here.
111-
assertThat(testRoot().contributedNotNsClientPlugins.keys).containsExactly(320, 810)
128+
assertThat(testRoot().contributedNotNsClientPlugins.keys).containsExactly(300, 320, 810)
112129
}
113130

114131
@Test

plugins/sync/src/main/kotlin/app/aaps/plugins/sync/di/SyncPluginsListModule.kt

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)