Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 /** | 5 /** |
| 6 * Classes and methods for enumerating and preparing tests. | 6 * Classes and methods for enumerating and preparing tests. |
| 7 * | 7 * |
| 8 * This library includes: | 8 * This library includes: |
| 9 * | 9 * |
| 10 * - Creating tests by listing all the Dart files in certain directories, | 10 * - Creating tests by listing all the Dart files in certain directories, |
| 11 * and creating [TestCase]s for those files that meet the relevant criteria. | 11 * and creating [TestCase]s for those files that meet the relevant criteria. |
| 12 * - Preparing tests, including copying files and frameworks to temporary | 12 * - Preparing tests, including copying files and frameworks to temporary |
| 13 * directories, and computing the command line and arguments to be run. | 13 * directories, and computing the command line and arguments to be run. |
| 14 */ | 14 */ |
| 15 import 'dart:async'; | 15 import 'dart:async'; |
| 16 import 'dart:io'; | 16 import 'dart:io'; |
| 17 import 'dart:math'; | |
| 18 | 17 |
| 19 import 'browser_test.dart'; | 18 import 'browser_test.dart'; |
| 20 import 'compiler_configuration.dart'; | 19 import 'compiler_configuration.dart'; |
| 21 import 'configuration.dart'; | 20 import 'configuration.dart'; |
| 22 import 'drt_updater.dart'; | 21 import 'drt_updater.dart'; |
| 23 import 'expectation.dart'; | 22 import 'expectation.dart'; |
| 24 import 'expectation_set.dart'; | 23 import 'expectation_set.dart'; |
| 25 import 'html_test.dart' as html_test; | 24 import 'html_test.dart' as html_test; |
| 26 import 'http_server.dart'; | 25 import 'http_server.dart'; |
| 27 import 'multitest.dart'; | 26 import 'multitest.dart'; |
| (...skipping 717 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 745 createTestCase( | 744 createTestCase( |
| 746 filePath, | 745 filePath, |
| 747 filePath, | 746 filePath, |
| 748 optionsFromFile['hasCompileError'] as bool, | 747 optionsFromFile['hasCompileError'] as bool, |
| 749 optionsFromFile['hasRuntimeError'] as bool, | 748 optionsFromFile['hasRuntimeError'] as bool, |
| 750 hasStaticWarning: optionsFromFile['hasStaticWarning'] as bool); | 749 hasStaticWarning: optionsFromFile['hasStaticWarning'] as bool); |
| 751 } | 750 } |
| 752 } | 751 } |
| 753 | 752 |
| 754 static Path _findPubspecYamlFile(Path filePath) { | 753 static Path _findPubspecYamlFile(Path filePath) { |
| 755 final existsCache = TestUtils.existsCache; | 754 var root = TestUtils.dartDir; |
| 756 | |
| 757 Path root = TestUtils.dartDir; | |
| 758 assert("$filePath".startsWith("$root")); | 755 assert("$filePath".startsWith("$root")); |
| 759 | 756 |
| 760 // We start with the parent directory of [filePath] and go up until | 757 // We start with the parent directory of [filePath] and go up until |
| 761 // the root directory (excluding the root). | 758 // the root directory (excluding the root). |
| 762 List<String> segments = filePath.directoryPath.relativeTo(root).segments(); | 759 var segments = filePath.directoryPath.relativeTo(root).segments(); |
|
Bill Hesse
2017/06/09 13:27:13
I don't agree with dropping the type here.
Bob Nystrom
2017/06/15 21:57:44
Added it back, though I personally prefer omitting
| |
| 763 while (segments.length > 0) { | 760 while (segments.length > 0) { |
| 764 var pubspecYamlPath = new Path(segments.join('/')).append('pubspec.yaml'); | 761 var pubspecYamlPath = new Path(segments.join('/')).append('pubspec.yaml'); |
| 765 if (existsCache.doesFileExist(pubspecYamlPath.toNativePath())) { | 762 if (TestUtils.existsCache.doesFileExist(pubspecYamlPath.toNativePath())) { |
| 766 return root.join(pubspecYamlPath); | 763 return root.join(pubspecYamlPath); |
| 767 } | 764 } |
| 768 segments.removeLast(); | 765 segments.removeLast(); |
| 769 } | 766 } |
| 767 | |
| 770 return null; | 768 return null; |
| 771 } | 769 } |
| 772 | 770 |
| 773 void enqueueTestCaseFromTestInformation(TestInformation info) { | 771 void enqueueTestCaseFromTestInformation(TestInformation info) { |
| 774 String testName = buildTestCaseDisplayName(suiteDir, info.originTestPath, | 772 String testName = buildTestCaseDisplayName(suiteDir, info.originTestPath, |
| 775 multitestName: info.optionsFromFile['isMultitest'] as bool | 773 multitestName: info.optionsFromFile['isMultitest'] as bool |
| 776 ? info.multitestKey | 774 ? info.multitestKey |
| 777 : ""); | 775 : ""); |
| 778 Set<Expectation> expectations = testExpectations.expectations(testName); | 776 var expectations = testExpectations.expectations(testName); |
|
Bill Hesse
2017/06/09 13:27:13
I don't agree with dropping the type here. I know
Bob Nystrom
2017/06/15 21:57:44
Re-added.
| |
| 779 if (info is HtmlTestInformation) { | 777 if (info is HtmlTestInformation) { |
| 780 enqueueHtmlTest(info, testName, expectations); | 778 enqueueHtmlTest(info, testName, expectations); |
| 781 return; | 779 return; |
| 782 } | 780 } |
| 781 | |
| 783 var optionsFromFile = info.optionsFromFile; | 782 var optionsFromFile = info.optionsFromFile; |
| 784 | 783 |
| 785 // If this test is inside a package, we will check if there is a | 784 // If this test is inside a package, we will check if there is a |
| 786 // pubspec.yaml file and if so, create a custom package root for it. | 785 // pubspec.yaml file and if so, create a custom package root for it. |
| 787 List<Command> baseCommands = <Command>[]; | |
| 788 Path packageRoot; | 786 Path packageRoot; |
| 789 Path packages; | 787 Path packages; |
| 790 | 788 |
| 791 if (optionsFromFile['packageRoot'] == null && | 789 if (optionsFromFile['packageRoot'] == null && |
| 792 optionsFromFile['packages'] == null) { | 790 optionsFromFile['packages'] == null) { |
| 793 if (configuration.packageRoot != null) { | 791 if (configuration.packageRoot != null) { |
| 794 packageRoot = new Path(configuration.packageRoot); | 792 packageRoot = new Path(configuration.packageRoot); |
| 795 optionsFromFile['packageRoot'] = packageRoot.toNativePath(); | 793 optionsFromFile['packageRoot'] = packageRoot.toNativePath(); |
| 796 } | 794 } |
| 797 if (configuration.packages != null) { | 795 if (configuration.packages != null) { |
| 798 Path packages = new Path(configuration.packages); | 796 Path packages = new Path(configuration.packages); |
| 799 optionsFromFile['packages'] = packages.toNativePath(); | 797 optionsFromFile['packages'] = packages.toNativePath(); |
| 800 } | 798 } |
| 801 } | 799 } |
| 802 if (configuration.compilerConfiguration.hasCompiler && | 800 if (configuration.compilerConfiguration.hasCompiler && |
| 803 expectCompileError(info)) { | 801 expectCompileError(info)) { |
| 804 // If a compile-time error is expected, and we're testing a | 802 // If a compile-time error is expected, and we're testing a |
| 805 // compiler, we never need to attempt to run the program (in a | 803 // compiler, we never need to attempt to run the program (in a |
| 806 // browser or otherwise). | 804 // browser or otherwise). |
| 807 enqueueStandardTest(baseCommands, info, testName, expectations); | 805 enqueueStandardTest(info, testName, expectations); |
| 808 } else if (configuration.runtime.isBrowser) { | 806 } else if (configuration.runtime.isBrowser) { |
| 809 if (info.optionsFromFile['isMultiHtmlTest'] as bool) { | 807 if (info.optionsFromFile['isMultiHtmlTest'] as bool) { |
| 810 // A browser multi-test has multiple expectations for one test file. | 808 // A browser multi-test has multiple expectations for one test file. |
| 811 // Find all the different sub-test expecations for one entire test file. | 809 // Find all the different sub-test expecations for one entire test file. |
| 812 var subtestNames = info.optionsFromFile['subtestNames'] as List<String>; | 810 var subtestNames = info.optionsFromFile['subtestNames'] as List<String>; |
| 813 var multiHtmlTestExpectations = <String, Set<Expectation>>{}; | 811 var multiHtmlTestExpectations = <String, Set<Expectation>>{}; |
| 814 for (var name in subtestNames) { | 812 for (var name in subtestNames) { |
| 815 var fullTestName = '$testName/$name'; | 813 var fullTestName = '$testName/$name'; |
| 816 multiHtmlTestExpectations[fullTestName] = | 814 multiHtmlTestExpectations[fullTestName] = |
| 817 testExpectations.expectations(fullTestName); | 815 testExpectations.expectations(fullTestName); |
| 818 } | 816 } |
| 819 enqueueBrowserTest(baseCommands, packageRoot, packages, info, testName, | 817 enqueueBrowserTest( |
| 820 multiHtmlTestExpectations); | 818 packageRoot, packages, info, testName, multiHtmlTestExpectations); |
| 821 } else { | 819 } else { |
| 822 enqueueBrowserTest( | 820 enqueueBrowserTest(packageRoot, packages, info, testName, expectations); |
| 823 baseCommands, packageRoot, packages, info, testName, expectations); | |
| 824 } | 821 } |
| 825 } else { | 822 } else { |
| 826 enqueueStandardTest(baseCommands, info, testName, expectations); | 823 enqueueStandardTest(info, testName, expectations); |
| 827 } | 824 } |
| 828 } | 825 } |
| 829 | 826 |
| 830 void enqueueStandardTest(List<Command> baseCommands, TestInformation info, | 827 void enqueueStandardTest( |
| 831 String testName, Set<Expectation> expectations) { | 828 TestInformation info, String testName, Set<Expectation> expectations) { |
| 832 var commonArguments = | 829 var commonArguments = |
| 833 commonArgumentsFromFile(info.filePath, info.optionsFromFile); | 830 commonArgumentsFromFile(info.filePath, info.optionsFromFile); |
| 834 | 831 |
| 835 var vmOptionsList = getVmOptions(info.optionsFromFile); | 832 var vmOptionsList = getVmOptions(info.optionsFromFile); |
| 836 assert(!vmOptionsList.isEmpty); | 833 assert(!vmOptionsList.isEmpty); |
| 837 | 834 |
| 838 for (var vmOptionsVariant = 0; | 835 for (var vmOptionsVariant = 0; |
| 839 vmOptionsVariant < vmOptionsList.length; | 836 vmOptionsVariant < vmOptionsList.length; |
| 840 vmOptionsVariant++) { | 837 vmOptionsVariant++) { |
| 841 var vmOptions = vmOptionsList[vmOptionsVariant]; | 838 var vmOptions = vmOptionsList[vmOptionsVariant]; |
| 842 var allVmOptions = vmOptions; | 839 var allVmOptions = vmOptions; |
| 843 if (!extraVmOptions.isEmpty) { | 840 if (!extraVmOptions.isEmpty) { |
| 844 allVmOptions = vmOptions.toList()..addAll(extraVmOptions); | 841 allVmOptions = vmOptions.toList()..addAll(extraVmOptions); |
| 845 } | 842 } |
| 846 | 843 |
| 847 var commands = baseCommands.toList(); | 844 var commands = |
| 848 commands.addAll( | 845 makeCommands(info, vmOptionsVariant, allVmOptions, commonArguments); |
| 849 makeCommands(info, vmOptionsVariant, allVmOptions, commonArguments)); | |
| 850 enqueueNewTestCase(new TestCase( | 846 enqueueNewTestCase(new TestCase( |
| 851 '$suiteName/$testName', commands, configuration, expectations, | 847 '$suiteName/$testName', commands, configuration, expectations, |
| 852 isNegative: isNegative(info), info: info)); | 848 isNegative: isNegative(info), info: info)); |
| 853 } | 849 } |
| 854 } | 850 } |
| 855 | 851 |
| 856 bool expectCompileError(TestInformation info) { | 852 bool expectCompileError(TestInformation info) { |
| 857 return info.hasCompileError || | 853 return info.hasCompileError || |
| 858 (configuration.isChecked && info.hasCompileErrorIfChecked); | 854 (configuration.isChecked && info.hasCompileErrorIfChecked); |
| 859 } | 855 } |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1023 * JavaScript version of the test, and copies the appropriate framework | 1019 * JavaScript version of the test, and copies the appropriate framework |
| 1024 * files to that directory. It creates a [BrowserTestCase], which has | 1020 * files to that directory. It creates a [BrowserTestCase], which has |
| 1025 * two sequential steps to be run by the [ProcessQueue] when the test is | 1021 * two sequential steps to be run by the [ProcessQueue] when the test is |
| 1026 * executed: a compilation step and an execution step, both with the | 1022 * executed: a compilation step and an execution step, both with the |
| 1027 * appropriate executable and arguments. The [expectations] object can be | 1023 * appropriate executable and arguments. The [expectations] object can be |
| 1028 * either a Set<String> if the test is a regular test, or a Map<String | 1024 * either a Set<String> if the test is a regular test, or a Map<String |
| 1029 * subTestName, Set<String>> if we are running a browser multi-test (one | 1025 * subTestName, Set<String>> if we are running a browser multi-test (one |
| 1030 * compilation and many browser runs). | 1026 * compilation and many browser runs). |
| 1031 */ | 1027 */ |
| 1032 void enqueueBrowserTest( | 1028 void enqueueBrowserTest( |
| 1033 List<Command> baseCommands, | |
| 1034 Path packageRoot, | 1029 Path packageRoot, |
| 1035 Path packages, | 1030 Path packages, |
| 1036 TestInformation info, | 1031 TestInformation info, |
| 1037 String testName, | 1032 String testName, |
| 1038 /* Set<Expectation> | Map<String, Set<Expectation>> */ dynamic | 1033 /* Set<Expectation> | Map<String, Set<Expectation>> */ dynamic |
| 1039 expectations) { | 1034 expectations) { |
| 1040 var badChars = new RegExp('[-=/]'); | 1035 var badChars = new RegExp('[-=/]'); |
| 1041 var vmOptionsList = getVmOptions(info.optionsFromFile); | 1036 var vmOptionsList = getVmOptions(info.optionsFromFile); |
| 1042 var multipleOptions = vmOptionsList.length > 1; | 1037 var multipleOptions = vmOptionsList.length > 1; |
| 1043 for (var vmOptions in vmOptionsList) { | 1038 for (var vmOptions in vmOptionsList) { |
| 1044 var optionsName = | 1039 var optionsName = |
| 1045 multipleOptions ? vmOptions.join('-').replaceAll(badChars, '') : ''; | 1040 multipleOptions ? vmOptions.join('-').replaceAll(badChars, '') : ''; |
| 1046 var tempDir = createOutputDirectory(info.filePath, optionsName); | 1041 var tempDir = createOutputDirectory(info.filePath, optionsName); |
| 1047 enqueueBrowserTestWithOptions(baseCommands, packageRoot, packages, info, | 1042 enqueueBrowserTestWithOptions(packageRoot, packages, info, testName, |
| 1048 testName, expectations, vmOptions, tempDir); | 1043 expectations, vmOptions, tempDir); |
| 1049 } | 1044 } |
| 1050 } | 1045 } |
| 1051 | 1046 |
| 1052 void enqueueBrowserTestWithOptions( | 1047 void enqueueBrowserTestWithOptions( |
| 1053 List<Command> baseCommands, | |
| 1054 Path packageRoot, | 1048 Path packageRoot, |
| 1055 Path packages, | 1049 Path packages, |
| 1056 TestInformation info, | 1050 TestInformation info, |
| 1057 String testName, | 1051 String testName, |
| 1058 /* Set<Expectation> | Map<String, Set<Expectation>> */ expectations, | 1052 /* Set<Expectation> | Map<String, Set<Expectation>> */ expectations, |
| 1059 List<String> vmOptions, | 1053 List<String> vmOptions, |
| 1060 String tempDir) { | 1054 String tempDir) { |
| 1061 // TODO(Issue 14651): If we're on dartium, we need to pass [packageRoot] | 1055 // TODO(Issue 14651): If we're on dartium, we need to pass [packageRoot] |
| 1062 // on to the browser (it may be test specific). | 1056 // on to the browser (it may be test specific). |
| 1063 | 1057 |
| 1064 var filePath = info.filePath; | 1058 var filePath = info.filePath; |
| 1065 var fileName = filePath.toString(); | 1059 var fileName = filePath.toString(); |
| 1066 | 1060 |
| 1067 var optionsFromFile = info.optionsFromFile; | 1061 var optionsFromFile = info.optionsFromFile; |
| 1068 | 1062 |
| 1069 var compilationTempDir = createCompilationOutputDirectory(info.filePath); | 1063 var compilationTempDir = createCompilationOutputDirectory(info.filePath); |
| 1070 | 1064 |
| 1071 var dartWrapperFilename = '$tempDir/test.dart'; | 1065 var dartWrapperFilename = '$tempDir/test.dart'; |
| 1072 var compiledDartWrapperFilename = '$compilationTempDir/test.js'; | 1066 var compiledDartWrapperFilename = '$compilationTempDir/test.js'; |
| 1073 | 1067 |
| 1074 String content = null; | 1068 String content = null; |
| 1075 var dir = filePath.directoryPath; | 1069 var dir = filePath.directoryPath; |
| 1076 var nameNoExt = filePath.filenameWithoutExtension; | 1070 var nameNoExt = filePath.filenameWithoutExtension; |
| 1077 | 1071 |
| 1078 var customHtmlPath = dir.append('$nameNoExt.html').toNativePath(); | 1072 var customHtmlPath = dir.append('$nameNoExt.html').toNativePath(); |
| 1079 var customHtml = new File(customHtmlPath); | 1073 var customHtml = new File(customHtmlPath); |
| 1080 | 1074 |
| 1081 // Construct the command(s) that compile all the inputs needed by the | 1075 // Construct the command(s) that compile all the inputs needed by the |
| 1082 // browser test. For running Dart in DRT, this will be noop commands. | 1076 // browser test. For running Dart in DRT, this will be noop commands. |
| 1083 var commands = baseCommands.toList(); | 1077 var commands = <Command>[]; |
| 1084 | 1078 |
| 1085 // Use existing HTML document if available. | 1079 // Use existing HTML document if available. |
| 1086 String htmlPath; | 1080 String htmlPath; |
| 1087 if (customHtml.existsSync()) { | 1081 if (customHtml.existsSync()) { |
| 1088 // If necessary, run the Polymer deploy steps. | 1082 // If necessary, run the Polymer deploy steps. |
| 1089 // TODO(jmesserly): this should be generalized for any tests that | 1083 // TODO(jmesserly): this should be generalized for any tests that |
| 1090 // require Pub deploy, not just polymer. | 1084 // require Pub deploy, not just polymer. |
| 1091 if (customHtml.readAsStringSync().contains('<!--polymer-test')) { | 1085 if (customHtml.readAsStringSync().contains('<!--polymer-test')) { |
| 1092 if (configuration.compiler != Compiler.none) { | 1086 if (configuration.compiler != Compiler.none) { |
| 1093 commands.add( | 1087 commands.add( |
| (...skipping 622 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1716 /// Used for testing packages in on off settings, i.e., we pass in the actual | 1710 /// Used for testing packages in on off settings, i.e., we pass in the actual |
| 1717 /// directory that we want to test. | 1711 /// directory that we want to test. |
| 1718 class PKGTestSuite extends StandardTestSuite { | 1712 class PKGTestSuite extends StandardTestSuite { |
| 1719 PKGTestSuite(Configuration configuration, Path directoryPath) | 1713 PKGTestSuite(Configuration configuration, Path directoryPath) |
| 1720 : super(configuration, directoryPath.filename, directoryPath, | 1714 : super(configuration, directoryPath.filename, directoryPath, |
| 1721 ["$directoryPath/.status"], | 1715 ["$directoryPath/.status"], |
| 1722 isTestFilePredicate: (f) => f.endsWith('_test.dart'), | 1716 isTestFilePredicate: (f) => f.endsWith('_test.dart'), |
| 1723 recursive: true); | 1717 recursive: true); |
| 1724 | 1718 |
| 1725 void enqueueBrowserTest( | 1719 void enqueueBrowserTest( |
| 1726 List<Command> baseCommands, | |
| 1727 Path packageRoot, | 1720 Path packageRoot, |
| 1728 packages, | 1721 packages, |
| 1729 TestInformation info, | 1722 TestInformation info, |
| 1730 String testName, | 1723 String testName, |
| 1731 /* Set<Expectation> | Map<String, Set<Expectation>> */ dynamic | 1724 /* Set<Expectation> | Map<String, Set<Expectation>> */ dynamic |
| 1732 expectations) { | 1725 expectations) { |
| 1733 var filePath = info.filePath; | 1726 var filePath = info.filePath; |
| 1734 var dir = filePath.directoryPath; | 1727 var dir = filePath.directoryPath; |
| 1735 var nameNoExt = filePath.filenameWithoutExtension; | 1728 var nameNoExt = filePath.filenameWithoutExtension; |
| 1736 var customHtmlPath = dir.append('$nameNoExt.html'); | 1729 var customHtmlPath = dir.append('$nameNoExt.html'); |
| 1737 var customHtml = new File(customHtmlPath.toNativePath()); | 1730 var customHtml = new File(customHtmlPath.toNativePath()); |
| 1738 if (!customHtml.existsSync()) { | 1731 if (!customHtml.existsSync()) { |
| 1739 super.enqueueBrowserTest( | 1732 super.enqueueBrowserTest( |
| 1740 baseCommands, packageRoot, packages, info, testName, expectations); | 1733 packageRoot, packages, info, testName, expectations); |
| 1741 } else { | 1734 } else { |
| 1742 var relativeHtml = customHtmlPath.relativeTo(TestUtils.dartDir); | 1735 var relativeHtml = customHtmlPath.relativeTo(TestUtils.dartDir); |
| 1743 var commands = baseCommands.toList(); | |
| 1744 var fullPath = _createUrlPathFromFile(customHtmlPath); | 1736 var fullPath = _createUrlPathFromFile(customHtmlPath); |
| 1745 | 1737 |
| 1746 commands.add(CommandBuilder.instance | 1738 var commands = [ |
| 1747 .getBrowserTestCommand(fullPath, configuration, !isNegative(info))); | 1739 CommandBuilder.instance |
| 1740 .getBrowserTestCommand(fullPath, configuration, !isNegative(info)) | |
| 1741 ]; | |
| 1748 var testDisplayName = '$suiteName/$testName'; | 1742 var testDisplayName = '$suiteName/$testName'; |
| 1749 enqueueNewTestCase(new BrowserTestCase( | 1743 enqueueNewTestCase(new BrowserTestCase( |
| 1750 testDisplayName, | 1744 testDisplayName, |
| 1751 commands, | 1745 commands, |
| 1752 configuration, | 1746 configuration, |
| 1753 expectations as Set<Expectation>, | 1747 expectations as Set<Expectation>, |
| 1754 info, | 1748 info, |
| 1755 isNegative(info), | 1749 isNegative(info), |
| 1756 relativeHtml.toNativePath())); | 1750 relativeHtml.toNativePath())); |
| 1757 } | 1751 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1807 | 1801 |
| 1808 bool isTestFile(String filename) { | 1802 bool isTestFile(String filename) { |
| 1809 // NOTE: We exclude tests and patch files for now. | 1803 // NOTE: We exclude tests and patch files for now. |
| 1810 return filename.endsWith(".dart") && | 1804 return filename.endsWith(".dart") && |
| 1811 !filename.endsWith("_test.dart") && | 1805 !filename.endsWith("_test.dart") && |
| 1812 !filename.contains("_internal/js_runtime/lib"); | 1806 !filename.contains("_internal/js_runtime/lib"); |
| 1813 } | 1807 } |
| 1814 | 1808 |
| 1815 bool get listRecursively => true; | 1809 bool get listRecursively => true; |
| 1816 } | 1810 } |
| 1817 | |
| 1818 class LastModifiedCache { | |
| 1819 Map<String, DateTime> _cache = <String, DateTime>{}; | |
| 1820 | |
| 1821 /** | |
| 1822 * Returns the last modified date of the given [uri]. | |
| 1823 * | |
| 1824 * The return value will be cached for future queries. If [uri] is a local | |
| 1825 * file, it's last modified [Date] will be returned. If the file does not | |
| 1826 * exist, null will be returned instead. | |
| 1827 * In case [uri] is not a local file, this method will always return | |
| 1828 * the current date. | |
| 1829 */ | |
| 1830 DateTime getLastModified(Uri uri) { | |
| 1831 if (uri.scheme == "file") { | |
| 1832 if (_cache.containsKey(uri.path)) { | |
| 1833 return _cache[uri.path]; | |
| 1834 } | |
| 1835 var file = new File(new Path(uri.path).toNativePath()); | |
| 1836 _cache[uri.path] = file.existsSync() ? file.lastModifiedSync() : null; | |
| 1837 return _cache[uri.path]; | |
| 1838 } | |
| 1839 return new DateTime.now(); | |
| 1840 } | |
| 1841 } | |
| 1842 | |
| 1843 class ExistsCache { | |
| 1844 Map<String, bool> _cache = <String, bool>{}; | |
| 1845 | |
| 1846 /** | |
| 1847 * Returns true if the file in [path] exists, false otherwise. | |
| 1848 * | |
| 1849 * The information will be cached. | |
| 1850 */ | |
| 1851 bool doesFileExist(String path) { | |
| 1852 if (!_cache.containsKey(path)) { | |
| 1853 _cache[path] = new File(path).existsSync(); | |
| 1854 } | |
| 1855 return _cache[path]; | |
| 1856 } | |
| 1857 } | |
| 1858 | |
| 1859 class TestUtils { | |
| 1860 /** | |
| 1861 * Any script using TestUtils must set dartDirUri to a file:// URI | |
| 1862 * pointing to the root of the Dart checkout. | |
| 1863 */ | |
| 1864 static void setDartDirUri(Uri uri) { | |
| 1865 dartDirUri = uri; | |
| 1866 dartDir = new Path(uri.toFilePath()); | |
| 1867 } | |
| 1868 | |
| 1869 static Random rand = new Random.secure(); | |
| 1870 static Uri dartDirUri; | |
| 1871 static Path dartDir; | |
| 1872 static LastModifiedCache lastModifiedCache = new LastModifiedCache(); | |
| 1873 static ExistsCache existsCache = new ExistsCache(); | |
| 1874 static Path currentWorkingDirectory = new Path(Directory.current.path); | |
| 1875 | |
| 1876 /** | |
| 1877 * Generates a random number. | |
| 1878 */ | |
| 1879 static int getRandomNumber() { | |
| 1880 return rand.nextInt(0xffffffff); | |
| 1881 } | |
| 1882 | |
| 1883 /** | |
| 1884 * Creates a directory using a [relativePath] to an existing | |
| 1885 * [base] directory if that [relativePath] does not already exist. | |
| 1886 */ | |
| 1887 static Directory mkdirRecursive(Path base, Path relativePath) { | |
| 1888 if (relativePath.isAbsolute) { | |
| 1889 base = new Path('/'); | |
| 1890 } | |
| 1891 Directory dir = new Directory(base.toNativePath()); | |
| 1892 assert(dir.existsSync()); | |
| 1893 var segments = relativePath.segments(); | |
| 1894 for (String segment in segments) { | |
| 1895 base = base.append(segment); | |
| 1896 if (base.toString() == "/$segment" && | |
| 1897 segment.length == 2 && | |
| 1898 segment.endsWith(':')) { | |
| 1899 // Skip the directory creation for a path like "/E:". | |
| 1900 continue; | |
| 1901 } | |
| 1902 dir = new Directory(base.toNativePath()); | |
| 1903 if (!dir.existsSync()) { | |
| 1904 dir.createSync(); | |
| 1905 } | |
| 1906 assert(dir.existsSync()); | |
| 1907 } | |
| 1908 return dir; | |
| 1909 } | |
| 1910 | |
| 1911 /** | |
| 1912 * Copy a [source] file to a new place. | |
| 1913 * Assumes that the directory for [dest] already exists. | |
| 1914 */ | |
| 1915 static Future copyFile(Path source, Path dest) { | |
| 1916 return new File(source.toNativePath()) | |
| 1917 .openRead() | |
| 1918 .pipe(new File(dest.toNativePath()).openWrite()); | |
| 1919 } | |
| 1920 | |
| 1921 static Future copyDirectory(String source, String dest) { | |
| 1922 source = new Path(source).toNativePath(); | |
| 1923 dest = new Path(dest).toNativePath(); | |
| 1924 | |
| 1925 var executable = 'cp'; | |
| 1926 var args = ['-Rp', source, dest]; | |
| 1927 if (Platform.operatingSystem == 'windows') { | |
| 1928 executable = 'xcopy'; | |
| 1929 args = [source, dest, '/e', '/i']; | |
| 1930 } | |
| 1931 return Process.run(executable, args).then((ProcessResult result) { | |
| 1932 if (result.exitCode != 0) { | |
| 1933 throw new Exception("Failed to execute '$executable " | |
| 1934 "${args.join(' ')}'."); | |
| 1935 } | |
| 1936 }); | |
| 1937 } | |
| 1938 | |
| 1939 static Future deleteDirectory(String path) { | |
| 1940 // We are seeing issues with long path names on windows when | |
| 1941 // deleting them. Use the system tools to delete our long paths. | |
| 1942 // See issue 16264. | |
| 1943 if (Platform.operatingSystem == 'windows') { | |
| 1944 var native_path = new Path(path).toNativePath(); | |
| 1945 // Running this in a shell sucks, but rmdir is not part of the standard | |
| 1946 // path. | |
| 1947 return Process | |
| 1948 .run('rmdir', ['/s', '/q', native_path], runInShell: true) | |
| 1949 .then((ProcessResult result) { | |
| 1950 if (result.exitCode != 0) { | |
| 1951 throw new Exception('Can\'t delete path $native_path. ' | |
| 1952 'This path might be too long'); | |
| 1953 } | |
| 1954 }); | |
| 1955 } else { | |
| 1956 var dir = new Directory(path); | |
| 1957 return dir.delete(recursive: true); | |
| 1958 } | |
| 1959 } | |
| 1960 | |
| 1961 static void deleteTempSnapshotDirectory(Configuration configuration) { | |
| 1962 if (configuration.compiler == Compiler.appJit || | |
| 1963 configuration.compiler == Compiler.precompiler) { | |
| 1964 var checked = configuration.isChecked ? '-checked' : ''; | |
| 1965 var strong = configuration.isStrong ? '-strong' : ''; | |
| 1966 var minified = configuration.isMinified ? '-minified' : ''; | |
| 1967 var csp = configuration.isCsp ? '-csp' : ''; | |
| 1968 var sdk = configuration.useSdk ? '-sdk' : ''; | |
| 1969 var dirName = "${configuration.compiler.name}" | |
| 1970 "$checked$strong$minified$csp$sdk"; | |
| 1971 var generatedPath = | |
| 1972 configuration.buildDirectory + "/generated_compilations/$dirName"; | |
| 1973 TestUtils.deleteDirectory(generatedPath); | |
| 1974 } | |
| 1975 } | |
| 1976 | |
| 1977 static final debugLogFilePath = new Path(".debug.log"); | |
| 1978 | |
| 1979 /// If a flaky test did fail, infos about it (i.e. test name, stdin, stdout) | |
| 1980 /// will be written to this file. | |
| 1981 /// | |
| 1982 /// This is useful for debugging flaky tests. When running on a buildbot, the | |
| 1983 /// file can be made visible in the waterfall UI. | |
| 1984 static const flakyFileName = ".flaky.log"; | |
| 1985 | |
| 1986 /// If test.py was invoked with '--write-test-outcome-log it will write | |
| 1987 /// test outcomes to this file. | |
| 1988 static const testOutcomeFileName = ".test-outcome.log"; | |
| 1989 | |
| 1990 static void ensureExists(String filename, Configuration configuration) { | |
| 1991 if (!configuration.listTests && !existsCache.doesFileExist(filename)) { | |
| 1992 throw "'$filename' does not exist"; | |
| 1993 } | |
| 1994 } | |
| 1995 | |
| 1996 static Path absolutePath(Path path) { | |
| 1997 if (!path.isAbsolute) { | |
| 1998 return currentWorkingDirectory.join(path); | |
| 1999 } | |
| 2000 return path; | |
| 2001 } | |
| 2002 | |
| 2003 static int shortNameCounter = 0; // Make unique short file names on Windows. | |
| 2004 | |
| 2005 static String getShortName(String path) { | |
| 2006 final PATH_REPLACEMENTS = const { | |
| 2007 "pkg_polymer_e2e_test_bad_import_test": "polymer_bi", | |
| 2008 "pkg_polymer_e2e_test_canonicalization_test": "polymer_c16n", | |
| 2009 "pkg_polymer_e2e_test_experimental_boot_test": "polymer_boot", | |
| 2010 "pkg_polymer_e2e_test_good_import_test": "polymer_gi", | |
| 2011 "tests_co19_src_Language_12_Expressions_14_Function_Invocation_": | |
| 2012 "co19_fn_invoke_", | |
| 2013 "tests_co19_src_LayoutTests_fast_css_getComputedStyle_getComputedStyle-": | |
| 2014 "co19_css_getComputedStyle_", | |
| 2015 "tests_co19_src_LayoutTests_fast_dom_Document_CaretRangeFromPoint_" | |
| 2016 "caretRangeFromPoint-": "co19_caretrangefrompoint_", | |
| 2017 "tests_co19_src_LayoutTests_fast_dom_Document_CaretRangeFromPoint_" | |
| 2018 "hittest-relative-to-viewport_": "co19_caretrange_hittest_", | |
| 2019 "tests_co19_src_LayoutTests_fast_dom_HTMLLinkElement_link-onerror-" | |
| 2020 "stylesheet-with-": "co19_dom_link-", | |
| 2021 "tests_co19_src_LayoutTests_fast_dom_": "co19_dom", | |
| 2022 "tests_co19_src_LayoutTests_fast_canvas_webgl": "co19_canvas_webgl", | |
| 2023 "tests_co19_src_LibTest_core_AbstractClassInstantiationError_" | |
| 2024 "AbstractClassInstantiationError_": "co19_abstract_class_", | |
| 2025 "tests_co19_src_LibTest_core_IntegerDivisionByZeroException_" | |
| 2026 "IntegerDivisionByZeroException_": "co19_division_by_zero", | |
| 2027 "tests_co19_src_WebPlatformTest_html_dom_documents_dom-tree-accessors_": | |
| 2028 "co19_dom_accessors_", | |
| 2029 "tests_co19_src_WebPlatformTest_html_semantics_embedded-content_" | |
| 2030 "media-elements_": "co19_media_elements", | |
| 2031 "tests_co19_src_WebPlatformTest_html_semantics_": "co19_semantics_", | |
| 2032 "tests_co19_src_WebPlatformTest_html-templates_additions-to-" | |
| 2033 "the-steps-to-clone-a-node_": "co19_htmltemplates_clone_", | |
| 2034 "tests_co19_src_WebPlatformTest_html-templates_definitions_" | |
| 2035 "template-contents-owner": "co19_htmltemplates_contents", | |
| 2036 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2037 "templates_additions-to-": "co19_htmltemplates_add_", | |
| 2038 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2039 "templates_appending-to-a-template_": "co19_htmltemplates_append_", | |
| 2040 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2041 "templates_clearing-the-stack-back-to-a-given-context_": | |
| 2042 "co19_htmltemplates_clearstack_", | |
| 2043 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2044 "templates_creating-an-element-for-the-token_": | |
| 2045 "co19_htmltemplates_create_", | |
| 2046 "tests_co19_src_WebPlatformTest_html-templates_template-element" | |
| 2047 "_template-": "co19_htmltemplates_element-", | |
| 2048 "tests_co19_src_WebPlatformTest_html-templates_": "co19_htmltemplate_", | |
| 2049 "tests_co19_src_WebPlatformTest_shadow-dom_shadow-trees_": | |
| 2050 "co19_shadow-trees_", | |
| 2051 "tests_co19_src_WebPlatformTest_shadow-dom_elements-and-dom-objects_": | |
| 2052 "co19_shadowdom_", | |
| 2053 "tests_co19_src_WebPlatformTest_shadow-dom_html-elements-in-" | |
| 2054 "shadow-trees_": "co19_shadow_html_", | |
| 2055 "tests_co19_src_WebPlatformTest_html_webappapis_system-state-and-" | |
| 2056 "capabilities_the-navigator-object": "co19_webappapis_navigator_", | |
| 2057 "tests_co19_src_WebPlatformTest_DOMEvents_approved_": "co19_dom_approved_" | |
| 2058 }; | |
| 2059 | |
| 2060 // Some tests are already in [build_dir]/generated_tests. | |
| 2061 String GEN_TESTS = 'generated_tests/'; | |
| 2062 if (path.contains(GEN_TESTS)) { | |
| 2063 int index = path.indexOf(GEN_TESTS) + GEN_TESTS.length; | |
| 2064 path = 'multitest/${path.substring(index)}'; | |
| 2065 } | |
| 2066 path = path.replaceAll('/', '_'); | |
| 2067 final int WINDOWS_SHORTEN_PATH_LIMIT = 58; | |
| 2068 final int WINDOWS_PATH_END_LENGTH = 30; | |
| 2069 if (Platform.operatingSystem == 'windows' && | |
| 2070 path.length > WINDOWS_SHORTEN_PATH_LIMIT) { | |
| 2071 for (var key in PATH_REPLACEMENTS.keys) { | |
| 2072 if (path.startsWith(key)) { | |
| 2073 path = path.replaceFirst(key, PATH_REPLACEMENTS[key]); | |
| 2074 break; | |
| 2075 } | |
| 2076 } | |
| 2077 if (path.length > WINDOWS_SHORTEN_PATH_LIMIT) { | |
| 2078 ++shortNameCounter; | |
| 2079 var pathEnd = path.substring(path.length - WINDOWS_PATH_END_LENGTH); | |
| 2080 path = "short${shortNameCounter}_$pathEnd"; | |
| 2081 } | |
| 2082 } | |
| 2083 return path; | |
| 2084 } | |
| 2085 } | |
| OLD | NEW |