blob: facc1c11811c4e90c67aa3e4bb530f54907d9dcc [file] [log] [blame]
// 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/widgets.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', () {
final TestTree 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);
});
test('subtreeDepth 1', () {
final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(subtreeDepth: 1));
expect(result.containsKey('properties'), isFalse);
final List<Map<String, Object?>> 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 List<Map<String, Object?>> 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 List<Map<String, Object?>> 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 List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(7));
expect(properties.every((Map<String, Object?> property) => property['foo'] == true), isTrue);
final List<Map<String, Object?>> 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 List<Map<String, Object?>> 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', () {
bool 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 List<Map<String, Object?>> 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 List<Map<String, Object?>> 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 List<Map<String, Object?>> 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 List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
expect(children, hasLength(3));
expect(children.last['truncated'], isTrue);
final List<Map<String, Object?>> 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 List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
expect(properties, hasLength(7));
expect(properties.every((Map<String, Object?> property) => !property.containsKey('properties')), isTrue);
final List<Map<String, Object?>> 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
void performRebuild() {
// Intentionally left empty.
}
@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) {
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,
);
}
}