Skip to content

Commit 143dc7c

Browse files
committed
Add metadata test
1 parent c09a432 commit 143dc7c

File tree

5 files changed

+190
-17
lines changed

5 files changed

+190
-17
lines changed

dwds/lib/src/debugging/inspector.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class AppInspector implements AppInspectorInterface {
114114

115115
// TODO(srujzs): We can invalidate these in a smarter way instead of
116116
// reinitializing when doing a hot reload, but these helpers recompute info
117-
// on demand and therefore are not in the critical path.
117+
// on demand later and therefore are not in the critical path.
118118
_classHelper = ClassHelper(this);
119119
_instanceHelper = InstanceHelper(this);
120120

@@ -729,7 +729,7 @@ class AppInspector implements AppInspectorInterface {
729729
/// This will get repopulated on restarts and reloads.
730730
///
731731
/// If [invalidatedModuleReport] is provided, only invalidates and
732-
/// recalculates caches for the invalidated libraries.
732+
/// recalculates caches for the invalidated/new libraries.
733733
///
734734
/// Returns the list of scripts refs cached.
735735
Future<List<ScriptRef>> _populateScriptCaches([
@@ -740,10 +740,13 @@ class AppInspector implements AppInspectorInterface {
740740
await globalToolConfiguration.loadStrategy
741741
.metadataProviderFor(appConnection.request.entrypointPath)
742742
.scripts;
743-
final invalidatedLibraries = invalidatedModuleReport?.deletedLibraries
743+
final invalidatedAndNewLibraries = invalidatedModuleReport
744+
?.deletedLibraries
744745
.union(invalidatedModuleReport.reloadedLibraries);
745-
if (invalidatedLibraries != null) {
746-
for (final libraryUri in invalidatedLibraries) {
746+
if (invalidatedAndNewLibraries != null) {
747+
// Invalidate any script caches that were computed for the now invalid
748+
// libraries. They will get repopulated later.
749+
for (final libraryUri in invalidatedAndNewLibraries) {
747750
final libraryRef = await _libraryHelper.libraryRefFor(libraryUri);
748751
final libraryId = libraryRef?.id;
749752
if (libraryId == null) continue;
@@ -768,8 +771,8 @@ class AppInspector implements AppInspectorInterface {
768771
isolate.libraries ?? <LibraryRef>[],
769772
);
770773
for (final uri in userLibraries) {
771-
if (invalidatedLibraries != null &&
772-
!invalidatedLibraries.contains(uri)) {
774+
if (invalidatedAndNewLibraries != null &&
775+
!invalidatedAndNewLibraries.contains(uri)) {
773776
continue;
774777
}
775778
final parts = scripts[uri];

dwds/lib/src/debugging/metadata/provider.dart

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ class MetadataProvider {
170170
return _moduleToModulePath.keys.toList();
171171
}
172172

173+
/// Compute metadata information after reading the metadata contents.
174+
///
175+
/// If [hotReload] is true, skips adding the SDK metadata and caches the
176+
/// computed [ModuleMetadata], returning the resulting cache.
177+
///
178+
/// Otherwise, adds all metadata and returns null.
173179
Future<Map<String, ModuleMetadata>?> _processMetadata(bool hotReload) async {
174180
final modules = hotReload ? <String, ModuleMetadata>{} : null;
175181
// The merged metadata resides next to the entrypoint.
@@ -211,10 +217,17 @@ class MetadataProvider {
211217
return modules;
212218
}
213219

220+
/// Process all metadata and compute caches once.
214221
Future<void> _initialize() async {
215222
await _metadataMemoizer.runOnce(() => _processMetadata(false));
216223
}
217224

225+
/// Given a map of hot reloaded modules mapped to their respective libraries,
226+
/// determines deleted and invalidated libraries and modules, invalidates them
227+
/// in any caches, and recomputes the necessary information.
228+
///
229+
/// Returns an [InvalidatedModuleReport] that can be used to invalidate other
230+
/// caches after a hot reload.
218231
Future<InvalidatedModuleReport> reinitializeAfterReload(
219232
Map<String, List> reloadedModulesToLibraries,
220233
) async {
@@ -318,12 +331,23 @@ class AbsoluteImportUriException implements Exception {
318331
String toString() => "AbsoluteImportUriError: '$importUri'";
319332
}
320333

334+
/// Computed after a hot reload using
335+
/// [MetadataProvider.reinitializeAfterReload], represents the modules and
336+
/// libraries in the program that were deleted, reloaded, and therefore,
337+
/// invalidated.
338+
///
339+
/// Used to recompute caches throughout DWDS.
321340
class InvalidatedModuleReport {
341+
/// Module names that are no longer in the program.
322342
final Set<String> deletedModules;
343+
344+
/// Library uris that are no longer in the program.
323345
final Set<String> deletedLibraries;
324-
// The union of invalidated and new modules.
346+
347+
/// Module names that were loaded during the hot reload.
325348
final Set<String> reloadedModules;
326-
// The union of invalidated and new libraries.
349+
350+
/// Library uris that were loaded during the hot reload.
327351
final Set<String> reloadedLibraries;
328352
InvalidatedModuleReport({
329353
required this.deletedModules,

dwds/test/fixtures/fakes.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -414,12 +414,11 @@ class FakeStrategy extends LoadStrategy {
414414
}
415415

416416
class FakeAssetReader implements AssetReader {
417-
final String? _metadata;
417+
String? metadata;
418418
final String? _dartSource;
419419
final String? _sourceMap;
420-
const FakeAssetReader({metadata, dartSource, sourceMap})
421-
: _metadata = metadata,
422-
_dartSource = dartSource,
420+
FakeAssetReader({this.metadata, dartSource, sourceMap})
421+
: _dartSource = dartSource,
423422
_sourceMap = sourceMap;
424423

425424
@override
@@ -432,7 +431,7 @@ class FakeAssetReader implements AssetReader {
432431

433432
@override
434433
Future<String> metadataContents(String serverPath) {
435-
return _throwUnimplementedOrReturnContents(_metadata);
434+
return _throwUnimplementedOrReturnContents(metadata);
436435
}
437436

438437
@override

dwds/test/fixtures/utilities.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,7 @@ class TestToolConfiguration extends ToolConfiguration {
196196
TestDebugSettings super.debugSettings =
197197
const TestDebugSettings.noDevTools(),
198198
TestBuildSettings buildSettings = const TestBuildSettings.dart(),
199-
}) : super(
200-
loadStrategy: TestStrategy(const FakeAssetReader(), buildSettings),
201-
);
199+
}) : super(loadStrategy: TestStrategy(FakeAssetReader(), buildSettings));
202200

203201
TestToolConfiguration.withLoadStrategy({
204202
TestAppMetadata super.appMetadata = const TestAppMetadata.externalApp(),

dwds/test/metadata_test.dart

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
@Timeout(Duration(minutes: 2))
66
library;
77

8+
import 'dart:convert';
9+
810
import 'package:dwds/src/debugging/metadata/module_metadata.dart';
911
import 'package:dwds/src/debugging/metadata/provider.dart';
1012
import 'package:test/test.dart';
@@ -115,4 +117,151 @@ void main() {
115117
expect(parts[0], 'org-dartlang-app:///web/main.dart');
116118
}
117119
});
120+
121+
String createMetadataContents(
122+
Map<String, List<String>> moduleToLibraries,
123+
Map<String, List<String>> libraryToParts,
124+
) {
125+
final contents = StringBuffer('');
126+
for (final module in moduleToLibraries.keys) {
127+
final moduleMetadata = ModuleMetadata(
128+
module,
129+
'load__web__$module',
130+
'foo/web/$module.ddc.js.map',
131+
'foo/web/$module.ddc.js',
132+
);
133+
for (final library in moduleToLibraries[module]!) {
134+
moduleMetadata.addLibrary(
135+
LibraryMetadata(library, library, libraryToParts[library] ?? []),
136+
);
137+
}
138+
contents.writeln(json.encode(moduleMetadata.toJson()));
139+
}
140+
contents.write('// intentionally empty: ...');
141+
return contents.toString();
142+
}
143+
144+
Future<void> verifyExpectations(
145+
MetadataProvider provider,
146+
Map<String, List<String>> moduleToLibraries,
147+
Map<String, List<String>> libraryToParts,
148+
) async {
149+
final scriptToModule = await provider.scriptToModule;
150+
final expectedScriptToModule = <String, String>{};
151+
for (final module in moduleToLibraries.keys) {
152+
final libraries = moduleToLibraries[module]!;
153+
for (final library in libraries) {
154+
expectedScriptToModule[library] = module;
155+
final parts = libraryToParts[library];
156+
if (parts != null) {
157+
for (final part in parts) {
158+
expectedScriptToModule[part] = module;
159+
}
160+
}
161+
}
162+
}
163+
for (final entry in expectedScriptToModule.entries) {
164+
expect(scriptToModule[entry.key], entry.value);
165+
}
166+
167+
final moduleToSourceMap = await provider.moduleToSourceMap;
168+
final expectedModuleToSourceMap = moduleToLibraries.keys.fold(
169+
<String, String>{},
170+
(map, module) {
171+
map[module] = 'foo/web/$module.ddc.js.map';
172+
return map;
173+
},
174+
);
175+
for (final entry in expectedModuleToSourceMap.entries) {
176+
expect(moduleToSourceMap[entry.key], entry.value);
177+
}
178+
179+
final modulePathToModule = await provider.modulePathToModule;
180+
final expectedModulePathToModule = moduleToLibraries.keys.fold(
181+
<String, String>{},
182+
(map, module) {
183+
map['foo/web/$module.ddc.js'] = module;
184+
return map;
185+
},
186+
);
187+
for (final entry in expectedModulePathToModule.entries) {
188+
expect(modulePathToModule[entry.key], entry.value);
189+
}
190+
191+
expect(await provider.modules, containsAll(moduleToLibraries.keys));
192+
}
193+
194+
test('reinitialize produces correct report', () async {
195+
final moduleToLibraries = <String, List<String>>{
196+
'm1': [
197+
'org-dartlang-app:///web/l1.dart',
198+
'org-dartlang-app:///web/l2.dart',
199+
],
200+
'm2': [
201+
'org-dartlang-app:///web/l3.dart',
202+
'org-dartlang-app:///web/l4.dart',
203+
],
204+
'm3': [
205+
'org-dartlang-app:///web/l5.dart',
206+
'org-dartlang-app:///web/l6.dart',
207+
],
208+
};
209+
final libraryToParts = <String, List<String>>{
210+
'l1': ['org-dartlang-app:///web/l1_p1.dart'],
211+
};
212+
final assetReader = FakeAssetReader(
213+
metadata: createMetadataContents(moduleToLibraries, libraryToParts),
214+
);
215+
final provider = MetadataProvider('foo.bootstrap.js', assetReader);
216+
await verifyExpectations(provider, moduleToLibraries, libraryToParts);
217+
218+
final newModuleToLibraries = <String, List<String>>{
219+
'm1': [
220+
'org-dartlang-app:///web/l1.dart',
221+
'org-dartlang-app:///web/l2.dart',
222+
],
223+
'm3': ['org-dartlang-app:///web/l3.dart'],
224+
'm4': [
225+
'org-dartlang-app:///web/l4.dart',
226+
'org-dartlang-app:///web/l7.dart',
227+
],
228+
};
229+
final newLibraryToParts = <String, List<String>>{
230+
'l1': ['org-dartlang-app:///web/l1_p1.dart'],
231+
'l7': ['org-dartlang-app:///web/l7_p1.dart'],
232+
};
233+
final reloadedModulesToLibraries = <String, List<String>>{
234+
'm3': ['org-dartlang-app:///web/l3.dart'],
235+
'm4': [
236+
'org-dartlang-app:///web/l4.dart',
237+
'org-dartlang-app:///web/l7.dart',
238+
],
239+
};
240+
assetReader.metadata = createMetadataContents(
241+
newModuleToLibraries,
242+
newLibraryToParts,
243+
);
244+
final invalidatedModuleReport = await provider.reinitializeAfterReload(
245+
reloadedModulesToLibraries,
246+
);
247+
expect(invalidatedModuleReport.deletedModules, ['m2']);
248+
expect(invalidatedModuleReport.deletedLibraries, [
249+
'org-dartlang-app:///web/l5.dart',
250+
'org-dartlang-app:///web/l6.dart',
251+
]);
252+
expect(
253+
invalidatedModuleReport.reloadedModules,
254+
reloadedModulesToLibraries.keys,
255+
);
256+
expect(
257+
invalidatedModuleReport.reloadedLibraries,
258+
containsAll(
259+
reloadedModulesToLibraries.values.fold<List<String>>([], (value, l) {
260+
value.addAll(l);
261+
return l;
262+
}),
263+
),
264+
);
265+
await verifyExpectations(provider, newModuleToLibraries, newLibraryToParts);
266+
});
118267
}

0 commit comments

Comments
 (0)