| 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 /** | 5 /** |
| 6 * A library for writing dart unit tests. | 6 * A library for writing dart unit tests. |
| 7 * | 7 * |
| 8 * ## Installing ## | 8 * ## Installing ## |
| 9 * | 9 * |
| 10 * Use [pub][] to install this package. Add the following to your `pubspec.yaml` | 10 * Use [pub][] to install this package. Add the following to your `pubspec.yaml` |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 */ | 166 */ |
| 167 library unittest; | 167 library unittest; |
| 168 | 168 |
| 169 import 'dart:async'; | 169 import 'dart:async'; |
| 170 import 'dart:collection'; | 170 import 'dart:collection'; |
| 171 import 'dart:isolate'; | 171 import 'dart:isolate'; |
| 172 import 'dart:math' show max; | 172 import 'dart:math' show max; |
| 173 import 'matcher.dart'; | 173 import 'matcher.dart'; |
| 174 export 'matcher.dart'; | 174 export 'matcher.dart'; |
| 175 | 175 |
| 176 import 'package:stack_trace/stack_trace.dart'; | |
| 177 | |
| 178 import 'src/utils.dart'; | |
| 179 part 'src/config.dart'; | 176 part 'src/config.dart'; |
| 180 part 'src/test_case.dart'; | 177 part 'src/test_case.dart'; |
| 181 | 178 |
| 182 Configuration _config; | 179 Configuration _config; |
| 183 | 180 |
| 184 /** | 181 /** |
| 185 * [Configuration] used by the unittest library. Note that if a | 182 * [Configuration] used by the unittest library. Note that if a |
| 186 * configuration has not been set, calling this getter will create | 183 * configuration has not been set, calling this getter will create |
| 187 * a default configuration. | 184 * a default configuration. |
| 188 */ | 185 */ |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 449 bool shouldCallBack() { | 446 bool shouldCallBack() { |
| 450 ++actualCalls; | 447 ++actualCalls; |
| 451 if (testCase.isComplete) { | 448 if (testCase.isComplete) { |
| 452 // Don't run if the test is done. We don't throw here as this is not | 449 // Don't run if the test is done. We don't throw here as this is not |
| 453 // the current test, but we do mark the old test as having an error | 450 // the current test, but we do mark the old test as having an error |
| 454 // if it previously passed. | 451 // if it previously passed. |
| 455 if (testCase.result == PASS) { | 452 if (testCase.result == PASS) { |
| 456 testCase.error( | 453 testCase.error( |
| 457 'Callback ${id}called ($actualCalls) after test case ' | 454 'Callback ${id}called ($actualCalls) after test case ' |
| 458 '${testCase.description} has already been marked as ' | 455 '${testCase.description} has already been marked as ' |
| 459 '${testCase.result}.'); | 456 '${testCase.result}.', ''); |
| 460 } | 457 } |
| 461 return false; | 458 return false; |
| 462 } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) { | 459 } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) { |
| 463 throw new TestFailure('Callback ${id}called more times than expected ' | 460 throw new TestFailure('Callback ${id}called more times than expected ' |
| 464 '($maxExpectedCalls).'); | 461 '($maxExpectedCalls).'); |
| 465 } | 462 } |
| 466 return true; | 463 return true; |
| 467 } | 464 } |
| 468 | 465 |
| 469 void after() { | 466 void after() { |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 670 runAsync(() { | 667 runAsync(() { |
| 671 _currentTestCaseIndex++; | 668 _currentTestCaseIndex++; |
| 672 _nextBatch(); | 669 _nextBatch(); |
| 673 }); | 670 }); |
| 674 } | 671 } |
| 675 | 672 |
| 676 /** | 673 /** |
| 677 * Utility function that can be used to notify the test framework that an | 674 * Utility function that can be used to notify the test framework that an |
| 678 * error was caught outside of this library. | 675 * error was caught outside of this library. |
| 679 */ | 676 */ |
| 680 void _reportTestError(String msg, trace) { | 677 void _reportTestError(String msg, String trace) { |
| 681 if (_currentTestCaseIndex < testCases.length) { | 678 if (_currentTestCaseIndex < testCases.length) { |
| 682 final testCase = testCases[_currentTestCaseIndex]; | 679 final testCase = testCases[_currentTestCaseIndex]; |
| 683 testCase.error(msg, trace); | 680 testCase.error(msg, trace); |
| 684 } else { | 681 } else { |
| 685 _uncaughtErrorMessage = "$msg: $trace"; | 682 _uncaughtErrorMessage = "$msg: $trace"; |
| 686 } | 683 } |
| 687 } | 684 } |
| 688 | 685 |
| 689 void rerunTests() { | 686 void rerunTests() { |
| 690 _uncaughtErrorMessage = null; | 687 _uncaughtErrorMessage = null; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 747 * Registers that an exception was caught for the current test. | 744 * Registers that an exception was caught for the current test. |
| 748 */ | 745 */ |
| 749 void registerException(e, [trace]) { | 746 void registerException(e, [trace]) { |
| 750 _registerException(currentTestCase, e, trace); | 747 _registerException(currentTestCase, e, trace); |
| 751 } | 748 } |
| 752 | 749 |
| 753 /** | 750 /** |
| 754 * Registers that an exception was caught for the current test. | 751 * Registers that an exception was caught for the current test. |
| 755 */ | 752 */ |
| 756 void _registerException(TestCase testCase, e, [trace]) { | 753 void _registerException(TestCase testCase, e, [trace]) { |
| 754 trace = trace == null ? '' : trace.toString(); |
| 757 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 755 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 758 if (testCase.result == null) { | 756 if (testCase.result == null) { |
| 759 testCase.fail(message, trace); | 757 testCase.fail(message, trace); |
| 760 } else { | 758 } else { |
| 761 testCase.error(message, trace); | 759 testCase.error(message, trace); |
| 762 } | 760 } |
| 763 } | 761 } |
| 764 | 762 |
| 765 /** | 763 /** |
| 766 * Runs a batch of tests, yielding whenever an asynchronous test starts | 764 * Runs a batch of tests, yielding whenever an asynchronous test starts |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 859 /** Enable a test by ID. */ | 857 /** Enable a test by ID. */ |
| 860 void enableTest(int testId) => _setTestEnabledState(testId, true); | 858 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 861 | 859 |
| 862 /** Disable a test by ID. */ | 860 /** Disable a test by ID. */ |
| 863 void disableTest(int testId) => _setTestEnabledState(testId, false); | 861 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 864 | 862 |
| 865 /** Signature for a test function. */ | 863 /** Signature for a test function. */ |
| 866 typedef dynamic TestFunction(); | 864 typedef dynamic TestFunction(); |
| 867 | 865 |
| 868 /** | 866 /** |
| 869 * A flag that controls whether we hide unittest and core library details in | 867 * A flag that controls whether we hide unittest details in exception stacks. |
| 870 * exception stacks. | |
| 871 * | |
| 872 * Useful to disable when debugging unittest or matcher customizations. | 868 * Useful to disable when debugging unittest or matcher customizations. |
| 873 */ | 869 */ |
| 874 bool formatStacks = true; | 870 bool formatStacks = true; |
| 875 | 871 |
| 876 /** Returns a Trace object from a StackTrace object or a String. */ | 872 // Stack formatting utility. Strips extraneous content from a stack trace. |
| 877 Trace _getTrace(stack) { | 873 // Stack frame lines are parsed with a regexp, which has been tested |
| 878 Trace trace; | 874 // in Chrome, Firefox and the VM. If a line fails to be parsed it is |
| 879 if (stack == null) return null; | 875 // included in the output to be conservative. |
| 880 if (stack is String) { | 876 // |
| 881 trace = new Trace.parse(stack); | 877 // The output stack consists of everything after the call to TestCase._run. |
| 882 } else if (stack is StackTrace) { | 878 // If we see an 'expect' in the frame we will prune everything above that |
| 883 trace = new Trace.from(stack); | 879 // as well. |
| 880 final _frameRegExp = new RegExp( |
| 881 r'^\s*' // Skip leading spaces. |
| 882 r'(?:' // Group of choices for the prefix. |
| 883 r'(?:#\d+\s*)|' // Skip VM's #<frameNumber>. |
| 884 r'(?:at )|' // Skip Firefox's 'at '. |
| 885 r'(?:))' // Other environments have nothing here. |
| 886 r'(.+)' // Extract the function/method. |
| 887 r'\s*[@\(]' // Skip space and @ or (. |
| 888 r'(' // This group of choices is for the source file. |
| 889 r'(?:.+:\/\/.+\/[^:]*)|' // Handle file:// or http:// URLs. |
| 890 r'(?:dart:[^:]*)|' // Handle dart:<lib>. |
| 891 r'(?:package:[^:]*)' // Handle package:<path> |
| 892 r'):([:\d]+)[\)]?$'); // Get the line number and optional column number. |
| 893 |
| 894 String _formatStack(stack) { |
| 895 if (!formatStacks) return "$stack"; |
| 896 var lines; |
| 897 if (stack is StackTrace) { |
| 898 lines = stack.toString().split('\n'); |
| 899 } else if (stack is String) { |
| 900 lines = stack.split('\n'); |
| 884 } else { | 901 } else { |
| 885 throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.'); | 902 return stack.toString(); |
| 886 } | 903 } |
| 887 | 904 |
| 888 if (!formatStacks) return trace; | 905 // Calculate the max width of first column so we can |
| 906 // pad to align the second columns. |
| 907 int padding = lines.fold(0, (n, line) { |
| 908 var match = _frameRegExp.firstMatch(line); |
| 909 if (match == null) return n; |
| 910 return max(n, match[1].length + 1); |
| 911 }); |
| 889 | 912 |
| 890 // Format the stack trace by removing everything above TestCase._runTest, | 913 // We remove all entries that have a location in unittest. |
| 891 // which is usually going to be irrelevant. Also fold together unittest and | 914 // We strip out anything before _nextBatch too. |
| 892 // core library calls so only the function the user called is visible. | 915 var sb = new StringBuffer(); |
| 893 return new Trace(trace.frames.takeWhile((frame) { | 916 for (var i = 0; i < lines.length; i++) { |
| 894 return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; | 917 var line = lines[i]; |
| 895 })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); | 918 if (line == '') continue; |
| 919 var match = _frameRegExp.firstMatch(line); |
| 920 if (match == null) { |
| 921 sb.write(line); |
| 922 sb.write('\n'); |
| 923 } else { |
| 924 var member = match[1]; |
| 925 var location = match[2]; |
| 926 var position = match[3]; |
| 927 if (member.indexOf('TestCase._runTest') >= 0) { |
| 928 // Don't include anything after this. |
| 929 break; |
| 930 } else if (member.indexOf('expect') >= 0) { |
| 931 // It looks like this was an expect() failure; |
| 932 // drop all the frames up to here. |
| 933 sb.clear(); |
| 934 } else { |
| 935 sb.write(member); |
| 936 // Pad second column to a fixed position. |
| 937 for (var j = 0; j <= padding - member.length; j++) { |
| 938 sb.write(' '); |
| 939 } |
| 940 sb.write(location); |
| 941 sb.write(' '); |
| 942 sb.write(position); |
| 943 sb.write('\n'); |
| 944 } |
| 945 } |
| 946 } |
| 947 return sb.toString(); |
| 896 } | 948 } |
| OLD | NEW |