diagnostics_json_test.dart 12.9 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12
// 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();

13
    final Map<String, Object?> json = element.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate());
14 15 16 17
    expect(json['widgetRuntimeType'], 'Placeholder');
    expect(json['stateful'], isFalse);
  });

18
  test('StatefulElement diagnostics are stateful', () {
19 20
    final Element element = StatefulElement(const Tooltip(message: 'foo'));

21
    final Map<String, Object?> json = element.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate());
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
    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', () {
70
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate());
71 72 73 74 75
      expect(result.containsKey('properties'), isFalse);
      expect(result.containsKey('children'), isFalse);
    });

    test('subtreeDepth 1', () {
76
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(subtreeDepth: 1));
77
      expect(result.containsKey('properties'), isFalse);
78
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
79 80 81 82 83 84
      expect(children[0].containsKey('children'), isFalse);
      expect(children[1].containsKey('children'), isFalse);
      expect(children[2].containsKey('children'), isFalse);
    });

    test('subtreeDepth 5', () {
85
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(subtreeDepth: 5));
86
      expect(result.containsKey('properties'), isFalse);
87
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
88 89 90 91 92 93
      expect(children[0]['children'], hasLength(0));
      expect(children[1]['children'], hasLength(3));
      expect(children[2]['children'], hasLength(0));
    });

    test('includeProperties', () {
94
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(includeProperties: true));
95 96 97 98 99
      expect(result.containsKey('children'), isFalse);
      expect(result['properties'], hasLength(7));
    });

    test('includeProperties with subtreedepth 1', () {
100
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate(
101 102 103 104
        includeProperties: true,
        subtreeDepth: 1,
      ));
      expect(result['properties'], hasLength(7));
105
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
106 107 108 109 110 111 112
      expect(children, hasLength(3));
      expect(children[0]['properties'], hasLength(0));
      expect(children[1]['properties'], hasLength(2));
      expect(children[2]['properties'], hasLength(1));
    });

    test('additionalNodeProperties', () {
113
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(const TestDiagnosticsSerializationDelegate(
114 115 116 117 118 119 120
        includeProperties: true,
        subtreeDepth: 1,
        additionalNodePropertiesMap: <String, Object>{
          'foo': true,
        },
      ));
      expect(result['foo'], isTrue);
121
      final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
122
      expect(properties, hasLength(7));
123
      expect(properties.every((Map<String, Object?> property) => property['foo'] == true), isTrue);
124

125
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
126
      expect(children, hasLength(3));
127
      expect(children.every((Map<String, Object?> child) => child['foo'] == true), isTrue);
128 129 130
    });

    test('filterProperties - sublist', () {
131
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate(
132 133 134
          includeProperties: true,
          propertyFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
            return nodes.whereType<StringProperty>().toList();
135
          },
136
      ));
137
      final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
138
      expect(properties, hasLength(3));
139
      expect(properties.every((Map<String, Object?> property) => property['type'] == 'StringProperty'), isTrue);
140 141 142 143
    });

    test('filterProperties - replace', () {
      bool replaced = false;
144
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate(
145 146 147 148 149 150 151 152 153
          includeProperties: true,
          propertyFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
            if (replaced) {
              return nodes;
            }
            replaced = true;
            return <DiagnosticsNode>[
              StringProperty('foo', 'bar'),
            ];
154
          },
155
      ));
156
      final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
157 158 159 160 161
      expect(properties, hasLength(1));
      expect(properties.single['name'], 'foo');
    });

    test('filterChildren - sublist', () {
162
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate(
163 164 165
          subtreeDepth: 1,
          childFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
            return nodes.where((DiagnosticsNode node) => node.getProperties().isEmpty).toList();
166
          },
167
      ));
168
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
169 170 171 172
      expect(children, hasLength(1));
    });

    test('filterChildren - replace', () {
173
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate(
174 175
          subtreeDepth: 1,
          childFilter: (List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
176
            return nodes.expand((DiagnosticsNode node) => node.getChildren()).toList();
177
          },
178
      ));
179
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
180 181 182 183 184
      expect(children, hasLength(3));
      expect(children.first['name'], 'child node B1');
    });

    test('nodeTruncator', () {
185
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate(
186 187
          subtreeDepth: 5,
          includeProperties: true,
188
          nodeTruncator: (List<DiagnosticsNode> nodes, DiagnosticsNode? owner) {
189
            return nodes.take(2).toList();
190
          },
191
      ));
192
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
193 194 195
      expect(children, hasLength(3));
      expect(children.last['truncated'], isTrue);

196
      final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
197 198 199 200 201
      expect(properties, hasLength(3));
      expect(properties.last['truncated'], isTrue);
    });

    test('delegateForAddingNodes', () {
202
      final Map<String, Object?> result = testTree.toDiagnosticsNode().toJsonMap(TestDiagnosticsSerializationDelegate(
203 204 205 206
          subtreeDepth: 5,
          includeProperties: true,
          nodeDelegator: (DiagnosticsNode node, DiagnosticsSerializationDelegate delegate) {
            return delegate.copyWith(includeProperties: false);
207
          },
208
      ));
209
      final List<Map<String, Object?>> properties = result['properties']! as List<Map<String, Object?>>;
210
      expect(properties, hasLength(7));
211
      expect(properties.every((Map<String, Object?> property) => !property.containsKey('properties')), isTrue);
212

213
      final List<Map<String, Object?>> children = result['children']! as List<Map<String, Object?>>;
214
      expect(children, hasLength(3));
215
      expect(children.every((Map<String, Object?> child) => !child.containsKey('properties')), isTrue);
216 217 218 219 220 221 222
    });
  });
}

class _TestElement extends Element {
  _TestElement() : super(const Placeholder());

223 224
  @override
  bool get debugDoingBuild => throw UnimplementedError();
225 226 227 228
}

class TestTree extends Object with DiagnosticableTreeMixin {
  TestTree({
229
    this.name = '',
230 231 232 233 234 235 236 237
    this.style,
    this.children = const <TestTree>[],
    this.properties = const <DiagnosticsNode>[],
  });

  final String name;
  final List<TestTree> children;
  final List<DiagnosticsNode> properties;
238
  final DiagnosticsTreeStyle? style;
239 240

  @override
241
  List<DiagnosticsNode> debugDescribeChildren() => <DiagnosticsNode>[
242
    for (final TestTree child in children)
243
      child.toDiagnosticsNode(
244 245
        name: 'child ${child.name}',
        style: child.style,
246 247
      ),
  ];
248 249 250 251

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
252
    if (style != null) {
253
      properties.defaultDiagnosticsTreeStyle = style!;
254
    }
255 256 257 258 259

    this.properties.forEach(properties.add);
  }
}

260 261 262 263
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);

264 265 266 267 268 269 270 271 272 273 274 275
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;
276 277 278 279
  final NodeFilter? childFilter;
  final NodeFilter? propertyFilter;
  final NodeTruncator? nodeTruncator;
  final NodeDelegator? nodeDelegator;
280 281 282 283 284 285 286 287 288

  @override
  Map<String, Object> additionalNodeProperties(DiagnosticsNode node) {
    return additionalNodePropertiesMap;
  }

  @override
  DiagnosticsSerializationDelegate delegateForNode(DiagnosticsNode node) {
    if (nodeDelegator != null) {
289
      return nodeDelegator!(node, this);
290 291 292 293 294 295 296 297 298
    }
    return subtreeDepth > 0 ? copyWith(subtreeDepth: subtreeDepth - 1) : this;
  }

  @override
  bool get expandPropertyValues => false;

  @override
  List<DiagnosticsNode> filterChildren(List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
299
    return childFilter?.call(nodes, owner) ?? nodes;
300 301 302 303
  }

  @override
  List<DiagnosticsNode> filterProperties(List<DiagnosticsNode> nodes, DiagnosticsNode owner) {
304
    return propertyFilter?.call(nodes, owner) ?? nodes;
305 306 307 308 309 310 311 312 313
  }

  @override
  final bool includeProperties;

  @override
  final int subtreeDepth;

  @override
314 315
  List<DiagnosticsNode> truncateNodesList(List<DiagnosticsNode> nodes, DiagnosticsNode? owner) {
    return nodeTruncator?.call(nodes, owner) ?? nodes;
316 317 318
  }

  @override
319
  DiagnosticsSerializationDelegate copyWith({int? subtreeDepth, bool? includeProperties}) {
320 321 322 323 324 325 326 327 328 329 330
    return TestDiagnosticsSerializationDelegate(
      includeProperties: includeProperties ?? this.includeProperties,
      subtreeDepth: subtreeDepth ?? this.subtreeDepth,
      additionalNodePropertiesMap: additionalNodePropertiesMap,
      childFilter: childFilter,
      propertyFilter: propertyFilter,
      nodeTruncator: nodeTruncator,
      nodeDelegator: nodeDelegator,
    );
  }
}