flutter_flutter/packages/flutter/test/foundation/diagnostics_json_test.dart
Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

398 lines
14 KiB
Dart

// Copyright 2014 The Flutter Authors. 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:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('Element diagnostics json includes widgetRuntimeType', () async {
final Element element = _TestElement();
final Map<String, Object?> json = element.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(),
);
expect(json['widgetRuntimeType'], 'Placeholder');
expect(json['stateful'], isFalse);
});
test('StatefulElement diagnostics are stateful', () {
final Element element = StatefulElement(const Tooltip(message: 'foo'));
final Map<String, Object?> json = element.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(),
);
expect(json['widgetRuntimeType'], 'Tooltip');
expect(json['stateful'], isTrue);
});
group('Serialization', () {
// These are always included.
const defaultDiagnosticKeys = <String>['description'];
// These are only included when fullDetails = false.
const essentialDiagnosticKeys = <String>['shouldIndent'];
// These are only included with fullDetails = true.
const detailedDiagnosticKeys = <String>['type', 'hasChildren', 'allowWrap'];
final testTree = TestTree(
properties: <DiagnosticsNode>[
StringProperty('stringProperty1', 'value1', quoted: false),
DoubleProperty('doubleProperty1', 42.5),
DoubleProperty('roundedProperty', 1.0 / 3.0),
StringProperty('DO_NOT_SHOW', 'DO_NOT_SHOW', level: DiagnosticLevel.hidden, quoted: false),
DiagnosticsProperty<Object>('DO_NOT_SHOW_NULL', null, defaultValue: null),
DiagnosticsProperty<Object>('nullProperty', null),
StringProperty('node_type', '<root node>', showName: false, quoted: false),
],
children: <TestTree>[
TestTree(name: 'node A'),
TestTree(
name: 'node B',
properties: <DiagnosticsNode>[
StringProperty('p1', 'v1', quoted: false),
StringProperty('p2', 'v2', quoted: false),
],
children: <TestTree>[
TestTree(name: 'node B1'),
TestTree(
name: 'node B2',
properties: <DiagnosticsNode>[StringProperty('property1', 'value1', quoted: false)],
),
TestTree(
name: 'node B3',
properties: <DiagnosticsNode>[
StringProperty('node_type', '<leaf node>', showName: false, quoted: false),
IntProperty('foo', 42),
],
),
],
),
TestTree(
name: 'node C',
properties: <DiagnosticsNode>[
StringProperty('foo', 'multi\nline\nvalue!', quoted: false),
],
),
],
);
test('default', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(),
);
expect(result.containsKey('properties'), isFalse);
expect(result.containsKey('children'), isFalse);
for (final keyName in defaultDiagnosticKeys) {
expect(result.containsKey(keyName), isTrue, reason: '$keyName is included.');
}
for (final keyName in detailedDiagnosticKeys) {
expect(result.containsKey(keyName), isTrue, reason: '$keyName is included.');
}
});
test('iterative implementation (without full details)', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMapIterative(
const DiagnosticsSerializationDelegate(),
);
expect(result.containsKey('properties'), isFalse);
expect(result.containsKey('children'), isFalse);
for (final keyName in defaultDiagnosticKeys) {
expect(result.containsKey(keyName), isTrue, reason: '$keyName is included.');
}
for (final keyName in essentialDiagnosticKeys) {
expect(result.containsKey(keyName), isTrue, reason: '$keyName is included.');
}
for (final keyName in detailedDiagnosticKeys) {
expect(result.containsKey(keyName), isFalse, reason: '$keyName is not included.');
}
// The truncated value should not be included if it is false.
expect(result['truncated'] == null || result['truncated'] == true, isTrue);
});
test('subtreeDepth 1', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(subtreeDepth: 1),
);
expect(result.containsKey('properties'), isFalse);
final children = result['children']! as List<Map<String, Object?>>;
expect(children[0].containsKey('children'), isFalse);
expect(children[1].containsKey('children'), isFalse);
expect(children[2].containsKey('children'), isFalse);
});
test('subtreeDepth 5', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(subtreeDepth: 5),
);
expect(result.containsKey('properties'), isFalse);
final children = result['children']! as List<Map<String, Object?>>;
expect(children[0]['children'], hasLength(0));
expect(children[1]['children'], hasLength(3));
expect(children[2]['children'], hasLength(0));
});
test('includeProperties', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(includeProperties: true),
);
expect(result.containsKey('children'), isFalse);
expect(result['properties'], hasLength(7));
});
test('includeProperties with subtreedepth 1', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
const DiagnosticsSerializationDelegate(includeProperties: true, subtreeDepth: 1),
);
expect(result['properties'], hasLength(7));
final children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(3));
expect(children[0]['properties'], hasLength(0));
expect(children[1]['properties'], hasLength(2));
expect(children[2]['properties'], hasLength(1));
});
test('additionalNodeProperties', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
const TestDiagnosticsSerializationDelegate(
includeProperties: true,
subtreeDepth: 1,
additionalNodePropertiesMap: <String, Object>{'foo': true},
),
);
expect(result['foo'], isTrue);
final properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(7));
expect(properties.every((Map<String, Object?> property) => property['foo'] == true), isTrue);
final children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(3));
expect(children.every((Map<String, Object?> child) => child['foo'] == true), isTrue);
});
test('filterProperties - sublist', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
TestDiagnosticsSerializationDelegate(
includeProperties: true,
propertyFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
return nodes.whereType<StringProperty>().toList();
},
),
);
final properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(3));
expect(
properties.every((Map<String, Object?> property) => property['type'] == 'StringProperty'),
isTrue,
);
});
test('filterProperties - replace', () {
var replaced = false;
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
TestDiagnosticsSerializationDelegate(
includeProperties: true,
propertyFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
if (replaced) {
return nodes;
}
replaced = true;
return <DiagnosticsNode>[StringProperty('foo', 'bar')];
},
),
);
final properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(1));
expect(properties.single['name'], 'foo');
});
test('filterChildren - sublist', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
TestDiagnosticsSerializationDelegate(
subtreeDepth: 1,
childFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
return nodes.where((DiagnosticsNode node) => node.getProperties().isEmpty).toList();
},
),
);
final children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(1));
});
test('filterChildren - replace', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
TestDiagnosticsSerializationDelegate(
subtreeDepth: 1,
childFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
return nodes.expand((DiagnosticsNode node) => node.getChildren()).toList();
},
),
);
final children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(3));
expect(children.first['name'], 'child node B1');
});
test('nodeTruncator', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
TestDiagnosticsSerializationDelegate(
subtreeDepth: 5,
includeProperties: true,
nodeTruncator: (List<DiagnosticsNode> nodes, DiagnosticsNode? owner) {
return nodes.take(2).toList();
},
),
);
final children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(3));
expect(children.last['truncated'], isTrue);
final properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(3));
expect(properties.last['truncated'], isTrue);
});
test('delegateForAddingNodes', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(
TestDiagnosticsSerializationDelegate(
subtreeDepth: 5,
includeProperties: true,
nodeDelegator: (DiagnosticsNode node, DiagnosticsSerializationDelegate delegate) {
return delegate.copyWith(includeProperties: false);
},
),
);
final properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(7));
expect(
properties.every((Map<String, Object?> property) => !property.containsKey('properties')),
isTrue,
);
final children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(3));
expect(
children.every((Map<String, Object?> child) => !child.containsKey('properties')),
isTrue,
);
});
});
}
class _TestElement extends Element {
_TestElement() : super(const Placeholder());
@override
bool get debugDoingBuild => throw UnimplementedError();
}
class TestTree extends Object with DiagnosticableTreeMixin {
TestTree({
this.name = '',
this.style,
this.children = const <TestTree>[],
this.properties = const <DiagnosticsNode>[],
});
final String name;
final List<TestTree> children;
final List<DiagnosticsNode> properties;
final DiagnosticsTreeStyle? style;
@override
List<DiagnosticsNode> debugDescribeChildren() => <DiagnosticsNode>[
for (final TestTree child in children)
child.toDiagnosticsNode(name: 'child ${child.name}', style: child.style),
];
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
if (style != null) {
properties.defaultDiagnosticsTreeStyle = style!;
}
this.properties.forEach(properties.add);
}
}
typedef NodeDelegator =
DiagnosticsSerializationDelegate Function(
DiagnosticsNode node,
TestDiagnosticsSerializationDelegate delegate,
);
typedef NodeTruncator =
List<DiagnosticsNode> Function(List<DiagnosticsNode> nodes, DiagnosticsNode? owner);
typedef NodeFilter =
List<DiagnosticsNode> Function(List<DiagnosticsNode> nodes, DiagnosticsNode owner);
class TestDiagnosticsSerializationDelegate implements DiagnosticsSerializationDelegate {
const TestDiagnosticsSerializationDelegate({
this.includeProperties = false,
this.subtreeDepth = 0,
this.additionalNodePropertiesMap = const <String, Object>{},
this.childFilter,
this.propertyFilter,
this.nodeTruncator,
this.nodeDelegator,
});
final Map<String, Object> additionalNodePropertiesMap;
final NodeFilter? childFilter;
final NodeFilter? propertyFilter;
final NodeTruncator? nodeTruncator;
final NodeDelegator? nodeDelegator;
@override
Map<String, Object> additionalNodeProperties(DiagnosticsNode node, {bool fullDetails = true}) {
return additionalNodePropertiesMap;
}
@override
DiagnosticsSerializationDelegate delegateForNode(DiagnosticsNode node) {
if (nodeDelegator != null) {
return nodeDelegator!(node, this);
}
return subtreeDepth > 0 ? copyWith(subtreeDepth: subtreeDepth - 1) : this;
}
@override
bool get expandPropertyValues => false;
@override
List<DiagnosticsNode> filterChildren(List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
return childFilter?.call(nodes, owner) ?? nodes;
}
@override
List<DiagnosticsNode> filterProperties(List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
return propertyFilter?.call(nodes, owner) ?? nodes;
}
@override
final bool includeProperties;
@override
final int subtreeDepth;
@override
List<DiagnosticsNode> truncateNodesList(List<DiagnosticsNode> nodes, DiagnosticsNode? owner) {
return nodeTruncator?.call(nodes, owner) ?? nodes;
}
@override
DiagnosticsSerializationDelegate copyWith({int? subtreeDepth, bool? includeProperties}) {
return TestDiagnosticsSerializationDelegate(
includeProperties: includeProperties ?? this.includeProperties,
subtreeDepth: subtreeDepth ?? this.subtreeDepth,
additionalNodePropertiesMap: additionalNodePropertiesMap,
childFilter: childFilter,
propertyFilter: propertyFilter,
nodeTruncator: nodeTruncator,
nodeDelegator: nodeDelegator,
);
}
}