| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:io'; | 5 import 'dart:io'; |
| 6 import 'drt_updater.dart'; | 6 import 'drt_updater.dart'; |
| 7 import 'test_suite.dart'; | 7 import 'test_suite.dart'; |
| 8 import 'path.dart'; | 8 import 'path.dart'; |
| 9 import 'compiler_configuration.dart' show CompilerConfiguration; | 9 import 'compiler_configuration.dart' show CompilerConfiguration; |
| 10 import 'runtime_configuration.dart' show RuntimeConfiguration; | 10 import 'runtime_configuration.dart' show RuntimeConfiguration; |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 390 'write_debug_log', | 390 'write_debug_log', |
| 391 'write_test_outcome_log', | 391 'write_test_outcome_log', |
| 392 ].toSet(); | 392 ].toSet(); |
| 393 | 393 |
| 394 /// Parses a list of strings as test options. | 394 /// Parses a list of strings as test options. |
| 395 /// | 395 /// |
| 396 /// Returns a list of configurations in which to run the tests. | 396 /// Returns a list of configurations in which to run the tests. |
| 397 /// Configurations are maps mapping from option keys to values. When | 397 /// Configurations are maps mapping from option keys to values. When |
| 398 /// encountering the first non-option string, the rest of the arguments are | 398 /// encountering the first non-option string, the rest of the arguments are |
| 399 /// stored in the returned Map under the 'rest' key. | 399 /// stored in the returned Map under the 'rest' key. |
| 400 List<Map> parse(List<String> arguments) { | 400 List<Map<String, dynamic>> parse(List<String> arguments) { |
| 401 // TODO(rnystrom): The builders on the buildbots still pass this even | 401 // TODO(rnystrom): The builders on the buildbots still pass this even |
| 402 // though it does nothing. Until those can be fixed, silently ignore the | 402 // though it does nothing. Until those can be fixed, silently ignore the |
| 403 // option. Remove this once the buildbot scripts are fixed. | 403 // option. Remove this once the buildbot scripts are fixed. |
| 404 if (arguments.contains("--failure-summary")) { | 404 if (arguments.contains("--failure-summary")) { |
| 405 arguments = arguments.where((arg) => arg != "--failure-summary").toList(); | 405 arguments = arguments.where((arg) => arg != "--failure-summary").toList(); |
| 406 print('Note: Ignoring unsupported "--failure-summary" option.'); | 406 print('Note: Ignoring unsupported "--failure-summary" option.'); |
| 407 } | 407 } |
| 408 | 408 |
| 409 var configuration = {}; | 409 var configuration = <String, dynamic>{}; |
| 410 | 410 |
| 411 // Fill in configuration with arguments passed to the test script. | 411 // Fill in configuration with arguments passed to the test script. |
| 412 for (var i = 0; i < arguments.length; i++) { | 412 for (var i = 0; i < arguments.length; i++) { |
| 413 var arg = arguments[i]; | 413 var arg = arguments[i]; |
| 414 | 414 |
| 415 // Help supersedes all other arguments. | 415 // Help supersedes all other arguments. |
| 416 if (arg == "--help" || arg == "-h") { | 416 if (arg == "--help" || arg == "-h") { |
| 417 _printHelp(); | 417 _printHelp(); |
| 418 return null; | 418 return null; |
| 419 } | 419 } |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 } | 558 } |
| 559 | 559 |
| 560 return arguments; | 560 return arguments; |
| 561 } | 561 } |
| 562 | 562 |
| 563 /// Determines if a particular configuration has a valid combination of | 563 /// Determines if a particular configuration has a valid combination of |
| 564 /// compiler and runtime elements. | 564 /// compiler and runtime elements. |
| 565 bool _isValidConfig(Map config) { | 565 bool _isValidConfig(Map config) { |
| 566 var isValid = true; | 566 var isValid = true; |
| 567 List<String> validRuntimes; | 567 List<String> validRuntimes; |
| 568 switch (config['compiler']) { | 568 switch (config['compiler'] as String) { |
| 569 case 'dart2js': | 569 case 'dart2js': |
| 570 // Note: by adding 'none' as a configuration, if the user | 570 // Note: by adding 'none' as a configuration, if the user |
| 571 // runs test.py -c dart2js -r drt,none the dart2js_none and | 571 // runs test.py -c dart2js -r drt,none the dart2js_none and |
| 572 // dart2js_drt will be duplicating work. If later we don't need 'none' | 572 // dart2js_drt will be duplicating work. If later we don't need 'none' |
| 573 // with dart2js, we should remove it from here. | 573 // with dart2js, we should remove it from here. |
| 574 validRuntimes = const [ | 574 validRuntimes = const [ |
| 575 'd8', | 575 'd8', |
| 576 'jsshell', | 576 'jsshell', |
| 577 'drt', | 577 'drt', |
| 578 'none', | 578 'none', |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 break; | 611 break; |
| 612 } | 612 } |
| 613 | 613 |
| 614 if (!validRuntimes.contains(config['runtime'])) { | 614 if (!validRuntimes.contains(config['runtime'])) { |
| 615 isValid = false; | 615 isValid = false; |
| 616 print("Warning: combination of compiler '${config['compiler']}' and " | 616 print("Warning: combination of compiler '${config['compiler']}' and " |
| 617 "runtime '${config['runtime']}' is invalid. " | 617 "runtime '${config['runtime']}' is invalid. " |
| 618 "Skipping this combination."); | 618 "Skipping this combination."); |
| 619 } | 619 } |
| 620 | 620 |
| 621 if (config['ie'] && Platform.operatingSystem != 'windows') { | 621 if ((config['ie'] as bool) && Platform.operatingSystem != 'windows') { |
| 622 isValid = false; | 622 isValid = false; |
| 623 print("Warning: cannot run Internet Explorer on non-Windows operating" | 623 print("Warning: cannot run Internet Explorer on non-Windows operating" |
| 624 " system."); | 624 " system."); |
| 625 } | 625 } |
| 626 | 626 |
| 627 if (config['shard'] < 1 || config['shard'] > config['shards']) { | 627 if ((config['shard'] as int) < 1 || |
| 628 (config['shard'] as int) > (config['shards'] as int)) { |
| 628 isValid = false; | 629 isValid = false; |
| 629 print("Error: shard index is ${config['shard']} out of " | 630 print("Error: shard index is ${config['shard']} out of " |
| 630 "${config['shards']} shards"); | 631 "${config['shards']} shards"); |
| 631 } | 632 } |
| 632 | 633 |
| 633 if (config['runtime'] == 'flutter' && config['flutter'] == '') { | 634 if (config['runtime'] == 'flutter' && config['flutter'] == '') { |
| 634 isValid = false; | 635 isValid = false; |
| 635 print("-rflutter requires the flutter engine executable to " | 636 print("-rflutter requires the flutter engine executable to " |
| 636 "be specified using --flutter="); | 637 "be specified using --flutter="); |
| 637 } | 638 } |
| 638 | 639 |
| 639 if (config['runtime'] == 'flutter' && config['arch'] != 'x64') { | 640 if (config['runtime'] == 'flutter' && config['arch'] != 'x64') { |
| 640 isValid = false; | 641 isValid = false; |
| 641 print("-rflutter is applicable only for --arch=x64"); | 642 print("-rflutter is applicable only for --arch=x64"); |
| 642 } | 643 } |
| 643 | 644 |
| 644 return isValid; | 645 return isValid; |
| 645 } | 646 } |
| 646 | 647 |
| 647 /// Recursively expands a configuration with multiple values per key into a | 648 /// Recursively expands a configuration with multiple values per key into a |
| 648 /// list of configurations with exactly one value per key. | 649 /// list of configurations with exactly one value per key. |
| 649 List<Map> _expandConfigurations(Map configuration) { | 650 List<Map<String, dynamic>> _expandConfigurations( |
| 651 Map<String, dynamic> configuration) { |
| 650 // Expand the pseudo-values such as 'all'. | 652 // Expand the pseudo-values such as 'all'. |
| 651 if (configuration['arch'] == 'all') { | 653 if (configuration['arch'] == 'all') { |
| 652 configuration['arch'] = 'ia32,x64,simarm,simarm64,simmips,simdbc64'; | 654 configuration['arch'] = 'ia32,x64,simarm,simarm64,simmips,simdbc64'; |
| 653 } | 655 } |
| 654 | 656 |
| 655 if (configuration['mode'] == 'all') { | 657 if (configuration['mode'] == 'all') { |
| 656 configuration['mode'] = 'debug,release,product'; | 658 configuration['mode'] = 'debug,release,product'; |
| 657 } | 659 } |
| 658 | 660 |
| 659 if (configuration['report_in_json']) { | 661 if (configuration['report_in_json'] as bool) { |
| 660 configuration['list'] = true; | 662 configuration['list'] = true; |
| 661 configuration['report'] = true; | 663 configuration['report'] = true; |
| 662 } | 664 } |
| 663 | 665 |
| 664 // Use verbose progress indication for verbose output unless buildbot | 666 // Use verbose progress indication for verbose output unless buildbot |
| 665 // progress indication is requested. | 667 // progress indication is requested. |
| 666 if (configuration['verbose'] && configuration['progress'] != 'buildbot') { | 668 if ((configuration['verbose'] as bool) && |
| 669 (configuration['progress'] as String) != 'buildbot') { |
| 667 configuration['progress'] = 'verbose'; | 670 configuration['progress'] = 'verbose'; |
| 668 } | 671 } |
| 669 | 672 |
| 670 // Create the artificial negative options that test status files | 673 // Create the artificial negative options that test status files |
| 671 // expect. | 674 // expect. |
| 672 configuration['unchecked'] = !configuration['checked']; | 675 configuration['unchecked'] = !(configuration['checked'] as bool); |
| 673 configuration['host_unchecked'] = !configuration['host_checked']; | 676 configuration['host_unchecked'] = !(configuration['host_checked'] as bool); |
| 674 configuration['unminified'] = !configuration['minified']; | 677 configuration['unminified'] = !(configuration['minified'] as bool); |
| 675 configuration['nocsp'] = !configuration['csp']; | 678 configuration['nocsp'] = !(configuration['csp'] as bool); |
| 676 | 679 |
| 677 String runtime = configuration['runtime']; | 680 var runtime = configuration['runtime'] as String; |
| 678 if (runtime == 'firefox') { | 681 if (runtime == 'firefox') { |
| 679 configuration['runtime'] == 'ff'; | 682 configuration['runtime'] == 'ff'; |
| 680 } | 683 } |
| 681 | 684 |
| 682 String compiler = configuration['compiler']; | 685 var compiler = configuration['compiler'] as String; |
| 683 configuration['browser'] = TestUtils.isBrowserRuntime(runtime); | 686 configuration['browser'] = TestUtils.isBrowserRuntime(runtime); |
| 684 configuration['analyzer'] = TestUtils.isCommandLineAnalyzer(compiler); | 687 configuration['analyzer'] = TestUtils.isCommandLineAnalyzer(compiler); |
| 685 | 688 |
| 686 // Set the javascript command line flag for less verbose status files. | 689 // Set the javascript command line flag for less verbose status files. |
| 687 configuration['jscl'] = TestUtils.isJsCommandLineRuntime(runtime); | 690 configuration['jscl'] = TestUtils.isJsCommandLineRuntime(runtime); |
| 688 | 691 |
| 689 // Allow suppression that is valid for all ie versions | 692 // Allow suppression that is valid for all ie versions |
| 690 configuration['ie'] = runtime.startsWith('ie'); | 693 configuration['ie'] = runtime.startsWith('ie'); |
| 691 | 694 |
| 692 // Expand the test selectors into a suite name and a simple | 695 // Expand the test selectors into a suite name and a simple |
| 693 // regular expressions to be used on the full path of a test file | 696 // regular expressions to be used on the full path of a test file |
| 694 // in that test suite. If no selectors are explicitly given use | 697 // in that test suite. If no selectors are explicitly given use |
| 695 // the default suite patterns. | 698 // the default suite patterns. |
| 696 var selectors = configuration['selectors']; | 699 var selectors = configuration['selectors']; |
| 697 if (selectors is! Map) { | 700 if (selectors is! Map) { |
| 698 if (selectors == null) { | 701 if (selectors == null) { |
| 699 if (configuration['suite_dir'] != null) { | 702 if (configuration['suite_dir'] != null) { |
| 700 var suite_path = new Path(configuration['suite_dir']); | 703 var suite_path = new Path(configuration['suite_dir'] as String); |
| 701 selectors = [suite_path.filename]; | 704 selectors = [suite_path.filename]; |
| 702 } else { | 705 } else { |
| 703 selectors = _defaultTestSelectors.toList(); | 706 selectors = _defaultTestSelectors.toList(); |
| 704 } | 707 } |
| 705 | 708 |
| 706 var excludeSuites = configuration['exclude_suite'] != null | 709 var excludeSuites = configuration['exclude_suite'] != null |
| 707 ? configuration['exclude_suite'].split(',') | 710 ? configuration['exclude_suite'].split(',') |
| 708 : []; | 711 : []; |
| 709 for (var exclude in excludeSuites) { | 712 for (var exclude in excludeSuites) { |
| 710 if (selectors.contains(exclude)) { | 713 if ((selectors as List).contains(exclude)) { |
| 711 selectors.remove(exclude); | 714 selectors.remove(exclude); |
| 712 } else { | 715 } else { |
| 713 print("Warning: default selectors does not contain $exclude"); | 716 print("Warning: default selectors does not contain $exclude"); |
| 714 } | 717 } |
| 715 } | 718 } |
| 716 } | 719 } |
| 717 var selectorMap = <String, RegExp>{}; | 720 var selectorMap = <String, RegExp>{}; |
| 718 for (var i = 0; i < selectors.length; i++) { | 721 for (var i = 0; i < (selectors as List).length; i++) { |
| 719 var pattern = selectors[i]; | 722 var pattern = selectors[i] as String; |
| 720 var suite = pattern; | 723 var suite = pattern; |
| 721 var slashLocation = pattern.indexOf('/'); | 724 var slashLocation = pattern.indexOf('/'); |
| 722 if (slashLocation != -1) { | 725 if (slashLocation != -1) { |
| 723 suite = pattern.substring(0, slashLocation); | 726 suite = pattern.substring(0, slashLocation); |
| 724 pattern = pattern.substring(slashLocation + 1); | 727 pattern = pattern.substring(slashLocation + 1); |
| 725 pattern = pattern.replaceAll('*', '.*'); | 728 pattern = pattern.replaceAll('*', '.*'); |
| 726 } else { | 729 } else { |
| 727 pattern = ".?"; | 730 pattern = ".?"; |
| 728 } | 731 } |
| 729 if (selectorMap.containsKey(suite)) { | 732 if (selectorMap.containsKey(suite)) { |
| 730 print("Error: '$suite/$pattern'. Only one test selection" | 733 print("Error: '$suite/$pattern'. Only one test selection" |
| 731 " pattern is allowed to start with '$suite/'"); | 734 " pattern is allowed to start with '$suite/'"); |
| 732 exit(1); | 735 exit(1); |
| 733 } | 736 } |
| 734 selectorMap[suite] = new RegExp(pattern); | 737 selectorMap[suite] = new RegExp(pattern); |
| 735 } | 738 } |
| 736 configuration['selectors'] = selectorMap; | 739 configuration['selectors'] = selectorMap; |
| 737 } | 740 } |
| 738 | 741 |
| 739 // Put observatory_ui in a configuration with its own packages override. | 742 // Put observatory_ui in a configuration with its own packages override. |
| 740 // Only one value in the configuration map is mutable: | 743 // Only one value in the configuration map is mutable: |
| 741 selectors = configuration['selectors']; | 744 selectors = configuration['selectors']; |
| 742 if (selectors.containsKey('observatory_ui')) { | 745 if ((selectors as Map<String, dynamic>).containsKey('observatory_ui')) { |
| 743 if (selectors.length == 1) { | 746 if (selectors.length == 1) { |
| 744 configuration['packages'] = TestUtils.dartDirUri | 747 configuration['packages'] = TestUtils.dartDirUri |
| 745 .resolve('runtime/observatory/.packages') | 748 .resolve('runtime/observatory/.packages') |
| 746 .toFilePath(); | 749 .toFilePath(); |
| 747 } else { | 750 } else { |
| 748 // Make a new configuration whose selectors map only contains | 751 // Make a new configuration whose selectors map only contains |
| 749 // observatory_ui, and remove the key from the original selectors. | 752 // observatory_ui, and remove the key from the original selectors. |
| 750 // The only mutable value in the map is the selectors, so a | 753 // The only mutable value in the map is the selectors, so a |
| 751 // shallow copy is safe. | 754 // shallow copy is safe. |
| 752 var observatoryConfiguration = new Map.from(configuration); | 755 var observatoryConfiguration = |
| 756 new Map<String, dynamic>.from(configuration); |
| 753 observatoryConfiguration['selectors'] = { | 757 observatoryConfiguration['selectors'] = { |
| 754 'observatory_ui': selectors['observatory_ui'] | 758 'observatory_ui': selectors['observatory_ui'] |
| 755 }; | 759 }; |
| 756 selectors.remove('observatory_ui'); | 760 selectors.remove('observatory_ui'); |
| 757 | 761 |
| 758 // Set the packages flag. | 762 // Set the packages flag. |
| 759 observatoryConfiguration['packages'] = TestUtils.dartDirUri | 763 observatoryConfiguration['packages'] = TestUtils.dartDirUri |
| 760 .resolve('runtime/observatory/.packages') | 764 .resolve('runtime/observatory/.packages') |
| 761 .toFilePath(); | 765 .toFilePath(); |
| 762 | 766 |
| 763 // Return the expansions of both configurations. Neither will reach | 767 // Return the expansions of both configurations. Neither will reach |
| 764 // this line in the recursive call to _expandConfigurations. | 768 // this line in the recursive call to _expandConfigurations. |
| 765 return _expandConfigurations(configuration) | 769 return _expandConfigurations(configuration) |
| 766 ..addAll(_expandConfigurations(observatoryConfiguration)); | 770 ..addAll(_expandConfigurations(observatoryConfiguration)); |
| 767 } | 771 } |
| 768 } | 772 } |
| 769 | 773 |
| 770 // Set the default package spec explicitly. | 774 // Set the default package spec explicitly. |
| 771 if (configuration['package_root'] == null && | 775 if (configuration['package_root'] == null && |
| 772 configuration['packages'] == null) { | 776 configuration['packages'] == null) { |
| 773 configuration['packages'] = | 777 configuration['packages'] = |
| 774 TestUtils.dartDirUri.resolve('.packages').toFilePath(); | 778 TestUtils.dartDirUri.resolve('.packages').toFilePath(); |
| 775 } | 779 } |
| 776 | 780 |
| 777 // Expand the architectures. | 781 // Expand the architectures. |
| 778 if (configuration['arch'].contains(',')) { | 782 if ((configuration['arch'] as String).contains(',')) { |
| 779 return _expandHelper('arch', configuration); | 783 return _expandHelper('arch', configuration); |
| 780 } | 784 } |
| 781 | 785 |
| 782 // Expand modes. | 786 // Expand modes. |
| 783 if (configuration['mode'].contains(',')) { | 787 if ((configuration['mode'] as String).contains(',')) { |
| 784 return _expandHelper('mode', configuration); | 788 return _expandHelper('mode', configuration); |
| 785 } | 789 } |
| 786 | 790 |
| 787 // Expand compilers. | 791 // Expand compilers. |
| 788 if (configuration['compiler'].contains(',')) { | 792 if ((configuration['compiler'] as String).contains(',')) { |
| 789 return _expandHelper('compiler', configuration); | 793 return _expandHelper('compiler', configuration); |
| 790 } | 794 } |
| 791 | 795 |
| 792 // Expand runtimes. | 796 // Expand runtimes. |
| 793 var runtimes = configuration['runtime']; | 797 var runtimes = configuration['runtime'] as String; |
| 794 if (runtimes.contains(',')) { | 798 if (runtimes.contains(',')) { |
| 795 return _expandHelper('runtime', configuration); | 799 return _expandHelper('runtime', configuration); |
| 796 } else { | 800 } else { |
| 797 // All runtimes eventually go through this path, after expansion. | 801 // All runtimes eventually go through this path, after expansion. |
| 798 var updater = runtimeUpdater(configuration); | 802 var updater = runtimeUpdater(configuration); |
| 799 if (updater != null) { | 803 if (updater != null) { |
| 800 updater.update(); | 804 updater.update(); |
| 801 } | 805 } |
| 802 } | 806 } |
| 803 | 807 |
| 804 // Adjust default timeout based on mode, compiler, and sometimes runtime. | 808 // Adjust default timeout based on mode, compiler, and sometimes runtime. |
| 805 if (configuration['timeout'] == -1) { | 809 if (configuration['timeout'] == -1) { |
| 806 var isReload = | 810 var isReload = (configuration['hot_reload'] as bool) || |
| 807 configuration['hot_reload'] || configuration['hot_reload_rollback']; | 811 (configuration['hot_reload_rollback'] as bool); |
| 808 int compilerMulitiplier = | 812 int compilerMulitiplier = |
| 809 new CompilerConfiguration(configuration).computeTimeoutMultiplier(); | 813 new CompilerConfiguration(configuration).computeTimeoutMultiplier(); |
| 810 int runtimeMultiplier = new RuntimeConfiguration(configuration) | 814 int runtimeMultiplier = new RuntimeConfiguration(configuration) |
| 811 .computeTimeoutMultiplier( | 815 .computeTimeoutMultiplier( |
| 812 mode: configuration['mode'], | 816 mode: configuration['mode'] as String, |
| 813 isChecked: configuration['checked'], | 817 isChecked: configuration['checked'] as bool, |
| 814 isReload: isReload, | 818 isReload: isReload, |
| 815 arch: configuration['arch']); | 819 arch: configuration['arch'] as String); |
| 816 configuration['timeout'] = 60 * compilerMulitiplier * runtimeMultiplier; | 820 configuration['timeout'] = 60 * compilerMulitiplier * runtimeMultiplier; |
| 817 } | 821 } |
| 818 | 822 |
| 819 return [configuration]; | 823 return [configuration]; |
| 820 } | 824 } |
| 821 | 825 |
| 822 /// Helper for _expandConfigurations. Creates a new configuration and adds it | 826 /// Helper for _expandConfigurations. Creates a new configuration and adds it |
| 823 /// to a list, for use in a case when a particular configuration has multiple | 827 /// to a list, for use in a case when a particular configuration has multiple |
| 824 /// results (separated by a ','). | 828 /// results (separated by a ','). |
| 825 /// Arguments: | 829 /// Arguments: |
| 826 /// option: The particular test option we are expanding. | 830 /// option: The particular test option we are expanding. |
| 827 /// configuration: The map containing all test configuration information | 831 /// configuration: The map containing all test configuration information |
| 828 /// specified. | 832 /// specified. |
| 829 List<Map> _expandHelper(String option, Map configuration) { | 833 List<Map<String, dynamic>> _expandHelper( |
| 830 var result = <Map>[]; | 834 String option, Map<String, dynamic> configuration) { |
| 835 var result = <Map<String, dynamic>>[]; |
| 831 var configs = configuration[option]; | 836 var configs = configuration[option]; |
| 832 for (var config in configs.split(',')) { | 837 for (var config in configs.split(',')) { |
| 833 var newConfiguration = new Map.from(configuration); | 838 var newConfiguration = new Map<String, dynamic>.from(configuration); |
| 834 newConfiguration[option] = config; | 839 newConfiguration[option] = config; |
| 835 result.addAll(_expandConfigurations(newConfiguration)); | 840 result.addAll(_expandConfigurations(newConfiguration)); |
| 836 } | 841 } |
| 837 return result; | 842 return result; |
| 838 } | 843 } |
| 839 | 844 |
| 840 /// Print out usage information. | 845 /// Print out usage information. |
| 841 void _printHelp() { | 846 void _printHelp() { |
| 842 var buffer = new StringBuffer(); | 847 var buffer = new StringBuffer(); |
| 843 | 848 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 907 if (name == option.name) return option; | 912 if (name == option.name) return option; |
| 908 | 913 |
| 909 // Allow hyphens instead of underscores as the separator since they are | 914 // Allow hyphens instead of underscores as the separator since they are |
| 910 // more common for command line flags. | 915 // more common for command line flags. |
| 911 if (name == option.name.replaceAll("_", "-")) return option; | 916 if (name == option.name.replaceAll("_", "-")) return option; |
| 912 } | 917 } |
| 913 | 918 |
| 914 return null; | 919 return null; |
| 915 } | 920 } |
| 916 } | 921 } |
| OLD | NEW |