| // Copyright (c) 2015, 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. |
| |
| /// Visualization of source mappings generated and tested in |
| /// 'source_mapping_test.dart'. |
| |
| library source_mapping.test.viewer; |
| |
| import 'dart:async'; |
| |
| import 'package:_fe_analyzer_shared/src/util/filenames.dart'; |
| |
| import '../helpers/sourcemap_helper.dart'; |
| import '../helpers/sourcemap_html_helper.dart'; |
| import '../helpers/sourcemap_html_templates.dart'; |
| import 'source_mapping_tester.dart'; |
| |
| const String DEFAULT_OUTPUT_PATH = 'out.js.map.html'; |
| |
| main(List<String> arguments) async { |
| bool measure = false; |
| bool missingOnly = false; |
| String outputPath = DEFAULT_OUTPUT_PATH; |
| Set<String> configurations = Set<String>(); |
| Map<String, Uri> tests = <String, Uri>{}; |
| for (String argument in arguments) { |
| if (argument.startsWith('-')) { |
| if (argument == '-m') { |
| // Measure instead of reporting the number of missing code points. |
| measure = true; |
| } else if (argument.startsWith('--out=')) { |
| // Generate visualization for the first configuration. |
| outputPath = argument.substring('--out='.length); |
| } else if (argument.startsWith('-o')) { |
| // Generate visualization for the first configuration. |
| outputPath = argument.substring('-o'.length); |
| } else if (argument == '--missing') { |
| missingOnly = true; |
| } else if (argument == '--file' || argument == '--exclude') { |
| // Handled by [parseArguments]. |
| } else { |
| print("Unknown option '$argument'."); |
| return; |
| } |
| } |
| } |
| |
| if (!parseArguments(arguments, configurations, tests, measure: measure)) { |
| return; |
| } |
| |
| OutputConfigurations outputConfigurations = OutputConfigurations( |
| configurations, |
| tests.keys, |
| ); |
| bool generateMultiConfigs = false; |
| if (configurations.length > 1 || tests.length > 1) { |
| for (String config in configurations) { |
| for (String file in tests.keys) { |
| String path = '$outputPath.$config.$file'; |
| Uri uri = Uri.base.resolve(nativeToUriPath(path)); |
| outputConfigurations.registerPathUri(config, file, path, uri); |
| } |
| } |
| generateMultiConfigs = true; |
| } else { |
| outputConfigurations.registerPathUri( |
| configurations.first, |
| tests.keys.first, |
| outputPath, |
| Uri.base.resolve(nativeToUriPath(outputPath)), |
| ); |
| } |
| |
| List<Measurement> measurements = <Measurement>[]; |
| for (String config in configurations) { |
| for (String file in tests.keys) { |
| List<String> options = TEST_CONFIGURATIONS[config]!; |
| Measurement measurement = await runTest( |
| config, |
| file, |
| tests[file]!, |
| options, |
| outputUri: outputConfigurations.getUri(config, file), |
| verbose: !measure, |
| missingOnly: missingOnly, |
| ); |
| measurements.add(measurement); |
| } |
| } |
| for (Measurement measurement in measurements) { |
| print(measurement); |
| } |
| if (generateMultiConfigs) { |
| outputMultiConfigs(Uri.base.resolve(outputPath), outputConfigurations); |
| } |
| } |
| |
| class OutputConfigurations implements Configurations { |
| @override |
| final Iterable<String> configs; |
| @override |
| final Iterable<String> files; |
| final Map<(String, String), String> pathMap = {}; |
| final Map<(String, String), Uri> uriMap = {}; |
| |
| OutputConfigurations(this.configs, this.files); |
| |
| void registerPathUri(String config, String file, String path, Uri uri) { |
| var key = (config, file); |
| pathMap[key] = path; |
| uriMap[key] = uri; |
| } |
| |
| Uri? getUri(String config, String file) => uriMap[(config, file)]; |
| |
| @override |
| String getPath(String config, String file) => pathMap[(config, file)]!; |
| } |
| |
| Future<Measurement> runTest( |
| String config, |
| String filename, |
| Uri uri, |
| List<String> options, { |
| Uri? outputUri, |
| required bool verbose, |
| bool missingOnly = false, |
| }) async { |
| TestResult result = await runTests( |
| config, |
| filename, |
| uri, |
| options, |
| verbose: verbose, |
| ); |
| if (outputUri != null) { |
| if (result.missingCodePointsMap.isNotEmpty) { |
| result.printMissingCodePoints(); |
| } |
| if (result.multipleNodesMap.isNotEmpty) { |
| result.printMultipleNodes(); |
| } |
| if (result.multipleOffsetsMap.isNotEmpty) { |
| result.printMultipleOffsets(); |
| } |
| List<SourceMapInfo> infoList = result.userInfoList; |
| if (missingOnly) { |
| infoList = infoList |
| .where((info) => result.missingCodePointsMap.containsKey(info)) |
| .toList(); |
| } |
| createTraceSourceMapHtml(outputUri, result.processor, infoList); |
| } |
| return Measurement( |
| config, |
| filename, |
| result.missingCodePointsMap.values.fold(0, (s, i) => s + i.length), |
| result.userInfoList.fold(0, (s, i) => s + i.codePoints.length), |
| ); |
| } |
| |
| class Measurement { |
| final String config; |
| final String filename; |
| |
| final int missing; |
| final int count; |
| |
| Measurement(this.config, this.filename, this.missing, this.count); |
| |
| @override |
| String toString() { |
| double percentage = 100 * missing / count; |
| return "Config '${config}', file: '${filename}': " |
| "$missing of $count ($percentage%) missing"; |
| } |
| } |