dart-sdk/tests/corelib/string_split_test.dart
Sigmund Cherem 050afd650f [expect] introduce Expect.throwsWhen and Expect.throwsTypeErrorWhen
Today, most tests that touch on a behavior variation end up
skipping expectations or the entirety of a test for some
testing configurations.  Moving forward, we'd like skip less
and try to account for the behavior variations if that's
reasonable.

This CL shows an approach to improve our test coverage for
behavior variations. We introduce two new methods to
[Expect] that allow us to conditionally check that a
function throws, depending on variation predicates.

The CL changes expectations for errors that don't occur
when dart2js omits parameter type checks or implicit
downcasts.

Note: originally I had the intention to introduce a name
parameter to `Expect.throws` and `Expect.throwsTypeError` to
avoid introducing a new API. However, because these APIs are
used for testing core language features, such as function
parameters themselves, we decided to keep the use of
features in these APIs as simple as it can be.

CoreLibraryReviewExempt: no public library semantic change - only improving test coverage under variations
Change-Id: I531657622655778491eaca8b37ba69ffaab559fc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/351340
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
2024-02-14 20:12:05 +00:00

141 lines
4.1 KiB
Dart

// Copyright (c) 2011, 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.
import "package:expect/expect.dart";
import "package:expect/variations.dart" as v;
main() {
testSplitString();
testSplitRegExp();
testSplitPattern();
}
testSplit(List<String> expect, String string, Pattern pattern) {
String patternString;
if (pattern is String) {
patternString = '"$pattern"';
} else if (pattern is RegExp) {
patternString = "/${pattern.pattern}/";
} else {
patternString = pattern.toString();
}
List actual = string.split(pattern);
// Ensure that the correct type is reified.
actual = actual as List<String>;
Expect.listEquals(expect, actual, '"$string".split($patternString)');
}
/** String patterns. */
void testSplitString() {
// Normal match.
testSplit(["a", "b", "c"], "a b c", " ");
testSplit(["a", "b", "c"], "adbdc", "d");
testSplit(["a", "b", "c"], "addbddc", "dd");
// No match.
testSplit(["abc"], "abc", " ");
testSplit(["a"], "a", "b");
testSplit([""], "", "b");
// Empty match matches everywhere except start/end.
testSplit(["a", "b", "c"], "abc", "");
// All empty parts.
testSplit(["", "", "", "", ""], "aaaa", "a");
testSplit(["", "", "", "", ""], " ", " ");
testSplit(["", ""], "a", "a");
// No overlapping matches. Match as early as possible.
testSplit(["", "", "", "a"], "aaaaaaa", "aa");
// Cannot split the empty string.
testSplit([], "", ""); // Match.
testSplit([""], "", "a"); // No match.
}
/** RegExp patterns. */
void testSplitRegExp() {
testSplitWithRegExp((s) => new RegExp(s));
}
/** Non-String, non-RegExp patterns. */
void testSplitPattern() {
testSplitWithRegExp((s) => new RegExpWrap(s));
}
void testSplitWithRegExp(makePattern) {
testSplit(["a", "b", "c"], "a b c", makePattern(r" "));
testSplit(["a", "b", "c"], "adbdc", makePattern(r"[dz]"));
testSplit(["a", "b", "c"], "addbddc", makePattern(r"dd"));
testSplit(["abc"], "abc", makePattern(r"b$"));
testSplit(["a", "b", "c"], "abc", makePattern(r""));
testSplit(["", "", "", ""], " ", makePattern(r"[ ]"));
// Non-zero-length match at end.
testSplit(["aa", ""], "aaa", makePattern(r"a$"));
// Zero-length match at end.
testSplit(["aaa"], "aaa", makePattern(r"$"));
// Non-zero-length match at start.
testSplit(["", "aa"], "aaa", makePattern(r"^a"));
// Zero-length match at start.
testSplit(["aaa"], "aaa", makePattern(r"^"));
// Picks first match, not longest or shortest.
testSplit(["", "", "", "a"], "aaaaaaa", makePattern(r"aa|aaa"));
testSplit(["", "", "", "a"], "aaaaaaa", makePattern(r"aa|"));
testSplit(["", "", "a"], "aaaaaaa", makePattern(r"aaa|aa"));
// Zero-width match depending on the following.
testSplit(["a", "bc"], "abc", makePattern(r"(?=[ab])"));
testSplit(["a", "b", "c"], "abc", makePattern(r"(?!^)"));
// Cannot split empty string.
testSplit([], "", makePattern(r""));
testSplit([], "", makePattern(r"(?:)"));
testSplit([], "", makePattern(r"$|(?=.)"));
testSplit([""], "", makePattern(r"a"));
// Can split singleton string if it matches.
testSplit(["", ""], "a", makePattern(r"a"));
testSplit(["a"], "a", makePattern(r"b"));
// Do not include captures.
testSplit(["a", "", "a"], "abba", makePattern(r"(b)"));
testSplit(["a", "a"], "abba", makePattern(r"(bb)"));
testSplit(["a", "a"], "abba", makePattern(r"(b*)"));
testSplit(["a", "a"], "aa", makePattern(r"(b*)"));
// But captures are still there, and do work with backreferences.
testSplit(["a", "cba"], "abcba", makePattern(r"([bc])(?=.*\1)"));
}
// A Pattern implementation with the same capabilities as a RegExp, but not
// directly recognizable as a RegExp.
class RegExpWrap implements Pattern {
final regexp;
RegExpWrap(String source) : regexp = new RegExp(source);
Iterable<Match> allMatches(String string, [int start = 0]) =>
regexp.allMatches(string, start);
Match matchAsPrefix(String string, [int start = 0]) =>
regexp.matchAsPrefix(string, start);
String toString() => "Wrap(/${regexp.pattern}/)";
}