blob: 50d12c8e1d13a3e6cf87c822d258bd23dca65498 [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@Timeout(Duration(minutes: 2))
library;
import 'dart:convert';
import 'package:dwds/src/debugging/metadata/module_metadata.dart';
import 'package:dwds/src/debugging/metadata/provider.dart';
import 'package:test/test.dart';
import 'fixtures/fakes.dart';
import 'fixtures/utilities.dart';
const _emptySourceMetadata =
'{"version":"1.0.0","name":"web/main","closureName":"load__web__main",'
'"sourceMapUri":"foo/web/main.ddc.js.map",'
'"moduleUri":"foo/web/main.ddc.js",'
'"libraries":[{"name":"main",'
'"importUri":"org-dartlang-app:///web/main.dart",'
'"fileUri":"org-dartlang-app:///web/main.dart","partUris":[]}]}\n'
'// intentionally empty: package blah has no dart sources';
const _fileUriMetadata =
'{"version":"1.0.0","name":"web/main","closureName":"load__web__main",'
'"sourceMapUri":"foo/web/main.ddc.js.map",'
'"moduleUri":"foo/web/main.ddc.js",'
'"libraries":[{"name":"main",'
'"importUri":"file:/Users/foo/blah/sample/lib/bar.dart",'
'"fileUri":"org-dartlang-app:///web/main.dart","partUris":[]}]}\n'
'// intentionally empty: package blah has no dart sources';
void main() {
final toolConfiguration = TestToolConfiguration.withLoadStrategy(
loadStrategy: FakeStrategy(FakeAssetReader()),
);
setGlobalsForTesting(toolConfiguration: toolConfiguration);
test('can parse metadata with empty sources', () async {
final provider = MetadataProvider(
'foo.bootstrap.js',
FakeAssetReader(metadata: _emptySourceMetadata),
);
expect(
await provider.libraries,
contains('org-dartlang-app:///web/main.dart'),
);
});
test('throws on metadata with absolute import uris', () async {
final provider = MetadataProvider(
'foo.bootstrap.js',
FakeAssetReader(metadata: _fileUriMetadata),
);
await expectLater(
provider.libraries,
throwsA(const TypeMatcher<AbsoluteImportUriException>()),
);
});
test(
'module name exists if useModuleName and otherwise use module uri',
() async {
final provider = MetadataProvider(
'foo.bootstrap.js',
FakeAssetReader(metadata: _emptySourceMetadata),
);
final modulePath = 'foo/web/main.ddc.js';
final moduleName = 'web/main';
final module = moduleName;
expect(
await provider.scriptToModule,
predicate<Map<String, String>>(
(scriptToModule) =>
!scriptToModule.values.any((value) => value == modulePath),
),
);
expect(await provider.moduleToSourceMap, {
module: 'foo/web/main.ddc.js.map',
});
expect(await provider.modulePathToModule, {modulePath: module});
expect(await provider.moduleToModulePath, {module: modulePath});
expect(await provider.modules, {module});
},
);
test('creates metadata from json', () async {
const json = {
'version': '1.0.0',
'name': 'web/main',
'closureName': 'load__web__main',
'sourceMapUri': 'foo/web/main.ddc.js.map',
'moduleUri': 'foo/web/main.ddc.js',
'libraries': [
{
'name': 'main',
'importUri': 'org-dartlang-app:///web/main.dart',
'partUris': ['org-dartlang-app:///web/main.dart'],
},
],
};
final metadata = ModuleMetadata.fromJson(json);
expect(metadata.version, '1.0.0');
expect(metadata.name, 'web/main');
expect(metadata.closureName, 'load__web__main');
expect(metadata.sourceMapUri, 'foo/web/main.ddc.js.map');
expect(metadata.moduleUri, 'foo/web/main.ddc.js');
final libraries = metadata.libraries;
expect(libraries.length, 1);
for (final lib in libraries.values) {
expect(lib.name, 'main');
expect(lib.importUri, 'org-dartlang-app:///web/main.dart');
final parts = lib.partUris;
expect(parts.length, 1);
expect(parts[0], 'org-dartlang-app:///web/main.dart');
}
});
String createMetadataContents(
Map<String, List<String>> moduleToLibraries,
Map<String, List<String>> libraryToParts,
) {
final contents = StringBuffer();
for (final MapEntry(key: module, value: libraries)
in moduleToLibraries.entries) {
final moduleMetadata = ModuleMetadata(
module,
'load__web__$module',
'foo/web/$module.ddc.js.map',
'foo/web/$module.ddc.js',
);
for (final library in libraries) {
moduleMetadata.addLibrary(
LibraryMetadata(library, library, libraryToParts[library] ?? []),
);
}
contents.writeln(json.encode(moduleMetadata.toJson()));
}
contents.write('// intentionally empty: ...');
return contents.toString();
}
Future<void> validateProvider(
MetadataProvider provider,
Map<String, List<String>> moduleToLibraries,
Map<String, List<String>> libraryToParts,
) async {
final expectedScriptToModule = <String, String>{};
final expectedModuleToSourceMap = <String, String>{};
final expectedModulePathToModule = <String, String>{};
final expectedModules = <String>{};
for (final MapEntry(key: module, value: libraries)
in moduleToLibraries.entries) {
for (final library in libraries) {
expectedScriptToModule[library] = module;
final parts = libraryToParts[library];
if (parts != null) {
for (final part in parts) {
expectedScriptToModule[part] = module;
}
}
}
expectedModuleToSourceMap[module] = 'foo/web/$module.ddc.js.map';
expectedModulePathToModule['foo/web/$module.ddc.js'] = module;
expectedModules.add(module);
}
final scriptToModule = await provider.scriptToModule;
for (final MapEntry(key: script, value: module)
in expectedScriptToModule.entries) {
expect(scriptToModule[script], module);
}
final moduleToSourceMap = await provider.moduleToSourceMap;
for (final MapEntry(key: module, value: sourceMap)
in expectedModuleToSourceMap.entries) {
expect(moduleToSourceMap[module], sourceMap);
}
final modulePathToModule = await provider.modulePathToModule;
for (final MapEntry(key: modulePath, value: module)
in expectedModulePathToModule.entries) {
expect(modulePathToModule[modulePath], module);
}
expect(await provider.modules, containsAll(expectedModules));
}
test('reinitialize produces correct ModifiedModuleReport', () async {
const moduleToLibraries = <String, List<String>>{
'm1': [
'org-dartlang-app:///web/l1.dart',
'org-dartlang-app:///web/l2.dart',
],
'm2': [
'org-dartlang-app:///web/l3.dart',
'org-dartlang-app:///web/l4.dart',
],
'm3': [
'org-dartlang-app:///web/l5.dart',
'org-dartlang-app:///web/l6.dart',
],
};
const libraryToParts = <String, List<String>>{
'org-dartlang-app:///web/l1.dart': ['org-dartlang-app:///web/l1_p1.dart'],
'org-dartlang-app:///web/l3.dart': ['org-dartlang-app:///web/l3_p1.dart'],
};
final assetReader = FakeAssetReader(
metadata: createMetadataContents(moduleToLibraries, libraryToParts),
);
final provider = MetadataProvider('foo.bootstrap.js', assetReader);
await validateProvider(provider, moduleToLibraries, libraryToParts);
const newModuleToLibraries = <String, List<String>>{
'm1': [
'org-dartlang-app:///web/l1.dart',
'org-dartlang-app:///web/l2.dart',
],
'm3': ['org-dartlang-app:///web/l3.dart'],
'm4': [
'org-dartlang-app:///web/l4.dart',
'org-dartlang-app:///web/l7.dart',
],
};
const newLibraryToParts = <String, List<String>>{
'org-dartlang-app:///web/l2.dart': ['org-dartlang-app:///web/l1_p1.dart'],
'org-dartlang-app:///web/l3.dart': ['org-dartlang-app:///web/l3_p2.dart'],
'org-dartlang-app:///web/l7.dart': ['org-dartlang-app:///web/l7_p1.dart'],
};
const reloadedModulesToLibraries = <String, List<String>>{
'm3': ['org-dartlang-app:///web/l3.dart'],
'm4': [
'org-dartlang-app:///web/l4.dart',
'org-dartlang-app:///web/l7.dart',
],
};
assetReader.metadata = createMetadataContents(
newModuleToLibraries,
newLibraryToParts,
);
final modifiedModuleReport = await provider.reinitializeAfterHotReload(
reloadedModulesToLibraries,
);
expect(modifiedModuleReport.deletedModules, ['m2']);
expect(
modifiedModuleReport.deletedLibraries,
unorderedEquals([
'org-dartlang-app:///web/l5.dart',
'org-dartlang-app:///web/l6.dart',
]),
);
expect(modifiedModuleReport.reloadedModules, ['m3', 'm4']);
expect(
modifiedModuleReport.reloadedLibraries,
unorderedEquals([
'org-dartlang-app:///web/l3.dart',
'org-dartlang-app:///web/l4.dart',
'org-dartlang-app:///web/l7.dart',
]),
);
expect(modifiedModuleReport.modifiedModules, ['m2', 'm3', 'm4']);
expect(
modifiedModuleReport.modifiedLibraries,
unorderedEquals([
'org-dartlang-app:///web/l3.dart',
'org-dartlang-app:///web/l4.dart',
'org-dartlang-app:///web/l5.dart',
'org-dartlang-app:///web/l6.dart',
'org-dartlang-app:///web/l7.dart',
]),
);
await validateProvider(provider, newModuleToLibraries, newLibraryToParts);
});
}