Unverified Commit f7c41d09 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Update `equalsIgnoringHashCodes` to take a list of Strings (#108507)

parent ec2621f7
......@@ -64,13 +64,18 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))');
expect(description[1], 'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))');
expect(description[2], 'checkColor: MaterialStatePropertyAll(Color(0xfffffff1))');
expect(description[3], 'overlayColor: MaterialStatePropertyAll(Color(0xfffffff2))');
expect(description[4], 'splashRadius: 1.0');
expect(description[5], 'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap');
expect(description[6], equalsIgnoringHashCodes('visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)'));
expect(
description,
equalsIgnoringHashCodes(<String>[
'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))',
'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))',
'checkColor: MaterialStatePropertyAll(Color(0xfffffff1))',
'overlayColor: MaterialStatePropertyAll(Color(0xfffffff2))',
'splashRadius: 1.0',
'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap',
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',
]),
);
});
testWidgets('Checkbox is themeable', (WidgetTester tester) async {
......
......@@ -2276,30 +2276,35 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'leading: Text');
expect(description[1], 'title: Text');
expect(description[2], 'subtitle: Text');
expect(description[3], 'trailing: Text');
expect(description[4], 'isThreeLine: THREE_LINE');
expect(description[5], 'dense: true');
expect(description[6], equalsIgnoringHashCodes('visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)'));
expect(description[7], 'shape: RoundedRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.zero)');
expect(description[8], 'style: ListTileStyle.list');
expect(description[9], 'selectedColor: Color(0xff0000ff)');
expect(description[10], 'iconColor: Color(0xff00ff00)');
expect(description[11], 'textColor: Color(0xffff0000)');
expect(description[12], 'contentPadding: EdgeInsets.zero');
expect(description[13], 'enabled: false');
expect(description[14], 'selected: true');
expect(description[15], 'focusColor: Color(0xff00ffff)');
expect(description[16], 'hoverColor: Color(0xff0000ff)');
expect(description[17], 'autofocus: true');
expect(description[18], 'tileColor: Color(0xffffff00)');
expect(description[19], 'selectedTileColor: Color(0xff123456)');
expect(description[20], 'enableFeedback: false');
expect(description[21], 'horizontalTitleGap: 4.0');
expect(description[22], 'minVerticalPadding: 2.0');
expect(description[23], 'minLeadingWidth: 6.0');
expect(
description,
equalsIgnoringHashCodes(<String>[
'leading: Text',
'title: Text',
'subtitle: Text',
'trailing: Text',
'isThreeLine: THREE_LINE',
'dense: true',
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',
'shape: RoundedRectangleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none), BorderRadius.zero)',
'style: ListTileStyle.list',
'selectedColor: Color(0xff0000ff)',
'iconColor: Color(0xff00ff00)',
'textColor: Color(0xffff0000)',
'contentPadding: EdgeInsets.zero',
'enabled: false',
'selected: true',
'focusColor: Color(0xff00ffff)',
'hoverColor: Color(0xff0000ff)',
'autofocus: true',
'tileColor: Color(0xffffff00)',
'selectedTileColor: Color(0xff123456)',
'enableFeedback: false',
'horizontalTitleGap: 4.0',
'minVerticalPadding: 2.0',
'minLeadingWidth: 6.0',
]),
);
});
group('Material 2', () {
......
......@@ -107,23 +107,25 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'dense: true');
expect(description[1], 'shape: StadiumBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none))');
expect(description[2], 'style: drawer');
expect(description[3], 'selectedColor: Color(0x00000001)');
expect(description[4], 'iconColor: Color(0x00000002)');
expect(description[5], 'textColor: Color(0x00000003)');
expect(description[6], 'contentPadding: EdgeInsets.all(100.0)');
expect(description[7], 'tileColor: Color(0x00000004)');
expect(description[8], 'selectedTileColor: Color(0x00000005)');
expect(description[9], 'horizontalTitleGap: 200.0');
expect(description[10], 'minVerticalPadding: 300.0');
expect(description[11], 'minLeadingWidth: 400.0');
expect(description[12], 'enableFeedback: true');
expect(description[13], 'mouseCursor: MaterialStateMouseCursor(clickable)');
expect(
description[14],
equalsIgnoringHashCodes('visualDensity: VisualDensity#00000(h: -1.0, v: -1.0)(horizontal: -1.0, vertical: -1.0)'),
description,
equalsIgnoringHashCodes(<String>[
'dense: true',
'shape: StadiumBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none))',
'style: drawer',
'selectedColor: Color(0x00000001)',
'iconColor: Color(0x00000002)',
'textColor: Color(0x00000003)',
'contentPadding: EdgeInsets.all(100.0)',
'tileColor: Color(0x00000004)',
'selectedTileColor: Color(0x00000005)',
'horizontalTitleGap: 200.0',
'minVerticalPadding: 300.0',
'minLeadingWidth: 400.0',
'enableFeedback: true',
'mouseCursor: MaterialStateMouseCursor(clickable)',
'visualDensity: VisualDensity#00000(h: -1.0, v: -1.0)(horizontal: -1.0, vertical: -1.0)',
]),
);
});
......
......@@ -61,12 +61,17 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))');
expect(description[1], 'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))');
expect(description[2], 'overlayColor: MaterialStatePropertyAll(Color(0xfffffff1))');
expect(description[3], 'splashRadius: 1.0');
expect(description[4], 'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap');
expect(description[5], equalsIgnoringHashCodes('visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)'));
expect(
description,
equalsIgnoringHashCodes(<String>[
'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))',
'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))',
'overlayColor: MaterialStatePropertyAll(Color(0xfffffff1))',
'splashRadius: 1.0',
'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap',
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',
]),
);
});
testWidgets('Radio is themeable', (WidgetTester tester) async {
......
......@@ -1009,8 +1009,13 @@ void main() {
.toList();
expect(description.length, equals(2));
expect(description[0], equalsIgnoringHashCodes('dispatcher: ActionDispatcher#00000'));
expect(description[1], equals('actions: {}'));
expect(
description,
equalsIgnoringHashCodes(<String>[
'dispatcher: ActionDispatcher#00000',
'actions: {}',
]),
);
});
testWidgets('Actions implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
......@@ -1032,8 +1037,13 @@ void main() {
.toList();
expect(description.length, equals(2));
expect(description[0], equalsIgnoringHashCodes('dispatcher: ActionDispatcher#00000'));
expect(description[1], equalsIgnoringHashCodes('actions: {TestIntent: TestAction#00000}'));
expect(
description,
equalsIgnoringHashCodes(<String>[
'dispatcher: ActionDispatcher#00000',
'actions: {TestIntent: TestAction#00000}',
]),
);
});
});
......
......@@ -292,11 +292,14 @@ Matcher offsetMoreOrLessEquals(Offset value, { double epsilon = precisionErrorTo
return _IsWithinDistance<Offset>(_offsetDistance, value, epsilon);
}
/// Asserts that two [String]s are equal after normalizing likely hash codes.
/// Asserts that two [String]s or `Iterable<String>`s are equal after
/// normalizing likely hash codes.
///
/// A `#` followed by 5 hexadecimal digits is assumed to be a short hash code
/// and is normalized to `#00000`.
///
/// Only [String] or `Iterable<String>` are allowed types for `value`.
///
/// See Also:
///
/// * [describeIdentity], a method that generates short descriptions of objects
......@@ -305,7 +308,8 @@ Matcher offsetMoreOrLessEquals(Offset value, { double epsilon = precisionErrorTo
/// [String] based on [Object.hashCode].
/// * [DiagnosticableTree.toStringDeep], a method that returns a [String]
/// typically containing multiple hash codes.
Matcher equalsIgnoringHashCodes(String value) {
Matcher equalsIgnoringHashCodes(Object value) {
assert(value is String || value is Iterable<String>, "Only String or Iterable<String> are allowed types for equalsIgnoringHashCodes, it doesn't accept ${value.runtimeType}");
return _EqualsIgnoringHashCodes(value);
}
......@@ -1056,21 +1060,33 @@ class _HasOneLineDescription extends Matcher {
}
class _EqualsIgnoringHashCodes extends Matcher {
_EqualsIgnoringHashCodes(String v) : _value = _normalize(v);
_EqualsIgnoringHashCodes(Object v) : _value = _normalize(v);
final String _value;
final Object _value;
static final Object _mismatchedValueKey = Object();
static String _normalize(String s) {
return s.replaceAll(RegExp(r'#[0-9a-fA-F]{5}'), '#00000');
static String _normalizeString(String value) {
return value.replaceAll(RegExp(r'#[\da-fA-F]{5}'), '#00000');
}
static Object _normalize(Object value, {bool expected = true}) {
if (value is String) {
return _normalizeString(value);
}
if (value is Iterable<String>) {
return value.map<String>((dynamic item) => _normalizeString(item.toString()));
}
throw ArgumentError('The specified ${expected ? 'expected' : 'comparison'} value for '
'equalsIgnoringHashCodes must be a String or an Iterable<String>, '
'not a ${value.runtimeType}');
}
@override
bool matches(dynamic object, Map<dynamic, dynamic> matchState) {
final String description = _normalize(object as String);
if (_value != description) {
matchState[_mismatchedValueKey] = description;
final Object normalized = _normalize(object as Object, expected: false);
if (!equals(_value).matches(normalized, matchState)) {
matchState[_mismatchedValueKey] = normalized;
return false;
}
return true;
......@@ -1078,7 +1094,10 @@ class _EqualsIgnoringHashCodes extends Matcher {
@override
Description describe(Description description) {
return description.add('multi line description equals $_value');
if (_value is String) {
return description.add('normalized value matches $_value');
}
return description.add('normalized value matches\n').addDescriptionOf(_value);
}
@override
......@@ -1089,14 +1108,14 @@ class _EqualsIgnoringHashCodes extends Matcher {
bool verbose,
) {
if (matchState.containsKey(_mismatchedValueKey)) {
final String actualValue = matchState[_mismatchedValueKey] as String;
final Object actualValue = matchState[_mismatchedValueKey] as Object;
// Leading whitespace is added so that lines in the multiline
// description returned by addDescriptionOf are all indented equally
// which makes the output easier to read for this case.
return mismatchDescription
.add('expected normalized value\n ')
.add('was expected to be normalized value\n')
.addDescriptionOf(_value)
.add('\nbut got\n ')
.add('\nbut got\n')
.addDescriptionOf(actualValue);
}
return mismatchDescription;
......@@ -1164,11 +1183,11 @@ class _HasGoodToStringDeep extends Matcher {
for (int i = 0; i < lines.length; ++i) {
final String line = lines[i];
if (line.isEmpty) {
issues.add('Line ${i+1} is empty.');
issues.add('Line ${i + 1} is empty.');
}
if (line.trimRight() != line) {
issues.add('Line ${i+1} has trailing whitespace.');
issues.add('Line ${i + 1} has trailing whitespace.');
}
}
......@@ -1179,11 +1198,11 @@ class _HasGoodToStringDeep extends Matcher {
// If a toStringDeep method doesn't properly handle nested values that
// contain line breaks it can fail to add the required prefixes to all
// lined when toStringDeep is called specifying prefixes.
const String prefixLineOne = 'PREFIX_LINE_ONE____';
const String prefixLineOne = 'PREFIX_LINE_ONE____';
const String prefixOtherLines = 'PREFIX_OTHER_LINES_';
final List<String> prefixIssues = <String>[];
String descriptionWithPrefixes =
object.toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines) as String; // ignore: avoid_dynamic_calls
// ignore: avoid_dynamic_calls
String descriptionWithPrefixes = object.toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines) as String;
if (descriptionWithPrefixes.endsWith('\n')) {
// Trim off trailing \n as the remaining calculations assume
// the description does not end with a trailing \n.
......@@ -1197,7 +1216,7 @@ class _HasGoodToStringDeep extends Matcher {
for (int i = 1; i < linesWithPrefixes.length; ++i) {
if (!linesWithPrefixes[i].startsWith(prefixOtherLines)) {
prefixIssues.add('Line ${i+1} does not contain the expected prefix.');
prefixIssues.add('Line ${i + 1} does not contain the expected prefix.');
}
}
......@@ -1979,9 +1998,9 @@ int _countDifferentPixels(Uint8List imageA, Uint8List imageB) {
int delta = 0;
for (int i = 0; i < imageA.length; i+=4) {
if (imageA[i] != imageB[i] ||
imageA[i+1] != imageB[i+1] ||
imageA[i+2] != imageB[i+2] ||
imageA[i+3] != imageB[i+3]) {
imageA[i + 1] != imageB[i + 1] ||
imageA[i + 2] != imageB[i + 2] ||
imageA[i + 3] != imageB[i + 3]) {
delta++;
}
}
......
......@@ -135,7 +135,7 @@ void main() {
);
});
test('normalizeHashCodesEquals', () {
test('equalsIgnoringHashCodes', () {
expect('Foo#34219', equalsIgnoringHashCodes('Foo#00000'));
expect('Foo#34219', equalsIgnoringHashCodes('Foo#12345'));
expect('Foo#34219', equalsIgnoringHashCodes('Foo#abcdf'));
......@@ -173,6 +173,24 @@ void main() {
expect('Foo#', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect('Foo#3421', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect('Foo#342193', isNot(equalsIgnoringHashCodes('Foo#00000')));
expect(<String>['Foo#a3b4d'], equalsIgnoringHashCodes(<String>['Foo#12345']));
expect(
<String>['Foo#a3b4d', 'Foo#12345'],
equalsIgnoringHashCodes(<String>['Foo#00000', 'Foo#00000']),
);
expect(
<String>['Foo#a3b4d', 'Bar#12345'],
equalsIgnoringHashCodes(<String>['Foo#00000', 'Bar#00000']),
);
expect(
<String>['Foo#a3b4d', 'Bar#12345'],
isNot(equalsIgnoringHashCodes(<String>['Bar#00000', 'Foo#00000'])),
);
expect(<String>['Foo#a3b4d'], isNot(equalsIgnoringHashCodes(<String>['Foo'])));
expect(
<String>['Foo#a3b4d'],
isNot(equalsIgnoringHashCodes(<String>['Foo#00000', 'Bar#00000'])),
);
});
test('moreOrLessEquals', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment