// 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. // @dart = 2.7 /// 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 'package:compiler/src/util/util.dart'; import 'source_mapping_tester.dart'; import '../helpers/sourcemap_helper.dart'; import '../helpers/sourcemap_html_helper.dart'; import '../helpers/sourcemap_html_templates.dart'; const String DEFAULT_OUTPUT_PATH = 'out.js.map.html'; main(List arguments) async { bool measure = false; bool missingOnly = false; String outputPath = DEFAULT_OUTPUT_PATH; Set configurations = new Set(); Map tests = {}; 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 = new OutputConfigurations(configurations, tests.keys); bool generateMultiConfigs = false; if (outputPath != null) { 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 measurements = []; for (String config in configurations) { for (String file in tests.keys) { List 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 configs; @override final Iterable files; final Map pathMap = {}; final Map uriMap = {}; OutputConfigurations(this.configs, this.files); void registerPathUri(String config, String file, String path, Uri uri) { Pair key = new Pair(config, file); pathMap[key] = path; uriMap[key] = uri; } Uri getUri(String config, String file) { Pair key = new Pair(config, file); return uriMap[key]; } @override String getPath(String config, String file) { Pair key = new Pair(config, file); return pathMap[key]; } } Future runTest( String config, String filename, Uri uri, List options, {Uri outputUri, 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 infoList = result.userInfoList; if (missingOnly) { infoList = infoList .where((info) => result.missingCodePointsMap.containsKey(info)) .toList(); } createTraceSourceMapHtml(outputUri, result.processor, infoList); } return new 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"; } }