Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
277001d1
Unverified
Commit
277001d1
authored
Nov 28, 2017
by
Jacob Richman
Committed by
GitHub
Nov 28, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Inspector JSON protocol to support Flutter IntelliJ Plugin tree view. (#12932)
parent
792e7ce8
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
999 additions
and
43 deletions
+999
-43
diagnostics.dart
packages/flutter/lib/src/foundation/diagnostics.dart
+93
-0
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+14
-0
widget_inspector.dart
packages/flutter/lib/src/widgets/widget_inspector.dart
+436
-9
diagnostics_test.dart
packages/flutter/test/foundation/diagnostics_test.dart
+213
-33
widget_inspector_test.dart
packages/flutter/test/widgets/widget_inspector_test.dart
+243
-1
No files found.
packages/flutter/lib/src/foundation/diagnostics.dart
View file @
277001d1
...
...
@@ -743,6 +743,33 @@ abstract class DiagnosticsNode {
String
get
_separator
=>
showSeparator
?
':'
:
''
;
/// Serialize the node excluding its descendents to a JSON map.
///
/// Subclasses should override if they have additional properties that are
/// useful for the GUI tools that consume this JSON.
///
/// See also:
///
/// * [WidgetInspectorService], which forms the bridge between JSON returned
/// by this method and interactive tree views in the Flutter IntelliJ
/// plugin.
@mustCallSuper
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
data
=
<
String
,
Object
>{
'name'
:
name
,
'showSeparator'
:
showSeparator
,
'description'
:
toDescription
(),
'level'
:
describeEnum
(
level
),
'showName'
:
showName
,
'emptyBodyDescription'
:
emptyBodyDescription
,
'style'
:
describeEnum
(
style
),
'valueToString'
:
value
.
toString
(),
'type'
:
runtimeType
.
toString
(),
'hasChildren'
:
getChildren
().
isNotEmpty
,
};
return
data
;
}
/// Returns a string representation of this diagnostic that is compatible with
/// the style of the parent if the node is not the root.
///
...
...
@@ -1054,6 +1081,13 @@ class StringProperty extends DiagnosticsProperty<String> {
/// Whether the value is enclosed in double quotes.
final
bool
quoted
;
@override
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
json
=
super
.
toJsonMap
();
json
[
'quoted'
]
=
quoted
;
return
json
;
}
@override
String
valueToString
({
TextTreeConfiguration
parentConfiguration
})
{
String
text
=
_description
??
value
;
...
...
@@ -1114,6 +1148,15 @@ abstract class _NumProperty<T extends num> extends DiagnosticsProperty<T> {
level:
level
,
);
@override
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
json
=
super
.
toJsonMap
();
if
(
unit
!=
null
)
json
[
'unit'
]
=
unit
;
json
[
'numberToString'
]
=
numberToString
();
return
json
;
}
/// Optional unit the [value] is measured in.
///
...
...
@@ -1326,6 +1369,17 @@ class FlagProperty extends DiagnosticsProperty<bool> {
level:
level
,
);
@override
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
json
=
super
.
toJsonMap
();
if
(
ifTrue
!=
null
)
json
[
'ifTrue'
]
=
ifTrue
;
if
(
ifFalse
!=
null
)
json
[
'ifFalse'
]
=
ifFalse
;
return
json
;
}
/// Description to use if the property [value] is true.
///
/// If not specified and [value] equals true the property's priority [level]
...
...
@@ -1443,6 +1497,15 @@ class IterableProperty<T> extends DiagnosticsProperty<Iterable<T>> {
return
DiagnosticLevel
.
fine
;
return
super
.
level
;
}
@override
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
json
=
super
.
toJsonMap
();
if
(
value
!=
null
)
{
json
[
'values'
]
=
value
.
map
<
String
>((
T
value
)
=>
value
.
toString
()).
toList
();
}
return
json
;
}
}
/// An property than displays enum values tersely.
...
...
@@ -1581,6 +1644,14 @@ class ObjectFlagProperty<T> extends DiagnosticsProperty<T> {
return
super
.
level
;
}
@override
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
json
=
super
.
toJsonMap
();
if
(
ifPresent
!=
null
)
json
[
'ifPresent'
]
=
ifPresent
;
return
json
;
}
}
/// Signature for computing the value of a property.
...
...
@@ -1683,6 +1754,28 @@ class DiagnosticsProperty<T> extends DiagnosticsNode {
final
String
_description
;
@override
Map
<
String
,
Object
>
toJsonMap
()
{
final
Map
<
String
,
Object
>
json
=
super
.
toJsonMap
();
if
(
defaultValue
!=
kNoDefaultValue
)
json
[
'defaultValue'
]
=
defaultValue
.
toString
();
if
(
ifEmpty
!=
null
)
json
[
'ifEmpty'
]
=
ifEmpty
;
if
(
ifNull
!=
null
)
json
[
'ifNull'
]
=
ifNull
;
if
(
tooltip
!=
null
)
json
[
'tooltip'
]
=
tooltip
;
json
[
'missingIfNull'
]
=
missingIfNull
;
if
(
exception
!=
null
)
json
[
'exception'
]
=
exception
.
toString
();
json
[
'propertyType'
]
=
propertyType
.
toString
();
json
[
'valueToString'
]
=
valueToString
();
json
[
'defaultLevel'
]
=
describeEnum
(
_defaultLevel
);
if
(
T
is
Diagnosticable
)
json
[
'isDiagnosticableValue'
]
=
true
;
return
json
;
}
/// Returns a string representation of the property value.
///
/// Subclasses should override this method instead of [toDescription] to
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
277001d1
...
...
@@ -3362,6 +3362,20 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
return
chain
.
join
(
'
\
u2190 '
);
}
/// Returns the parent chain from this element back to the root of the tree.
///
/// Useful for debug display of a tree of Elements with only nodes in the path
/// from the root to this Element expanded.
List
<
Element
>
debugGetDiagnosticChain
()
{
final
List
<
Element
>
chain
=
<
Element
>[
this
];
Element
node
=
_parent
;
while
(
node
!=
null
)
{
chain
.
add
(
node
);
node
=
node
.
_parent
;
}
return
chain
;
}
/// A short, textual description of this element.
@override
String
toStringShort
()
{
return
widget
!=
null
?
'
${widget.toStringShort()}
'
:
'[
$runtimeType
]'
;
...
...
packages/flutter/lib/src/widgets/widget_inspector.dart
View file @
277001d1
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'dart:collection'
;
import
'dart:convert'
;
import
'dart:developer'
as
developer
;
import
'dart:math'
as
math
;
import
'dart:ui'
as
ui
show
window
,
Picture
,
SceneBuilder
,
PictureRecorder
;
...
...
@@ -21,6 +22,383 @@ import 'gesture_detector.dart';
/// [WidgetInspector.selectButtonBuilder].
typedef
Widget
InspectorSelectButtonBuilder
(
BuildContext
context
,
VoidCallback
onPressed
);
/// A class describing a step along a path through a tree of [DiagnosticsNode]
/// objects.
///
/// This class is used to bundle all data required to display the tree with just
/// the nodes along a path expanded into a single JSON payload.
class
_DiagnosticsPathNode
{
/// Creates a full description of a step in a path through a tree of
/// [DiagnosticsNode] objects.
///
/// The [node] and [child] arguments must not be null.
_DiagnosticsPathNode
({
@required
this
.
node
,
@required
this
.
children
,
this
.
childIndex
})
:
assert
(
node
!=
null
),
assert
(
children
!=
null
);
/// Node at the point in the path this [_DiagnosticsPathNode] is describing.
final
DiagnosticsNode
node
;
/// Children of the [node] being described.
///
/// This value is cached instead of relying on `node.getChildren()` as that
/// method call might create new [DiagnosticsNode] objects for each child
/// and we would prefer to use the identical [DiagnosticsNode] for each time
/// a node exists in the path.
final
List
<
DiagnosticsNode
>
children
;
/// Index of the child that the path continues on.
///
/// Equal to `null` if the path does not continue.
final
int
childIndex
;
}
List
<
_DiagnosticsPathNode
>
_followDiagnosticableChain
(
List
<
Diagnosticable
>
chain
,
{
String
name
,
DiagnosticsTreeStyle
style
,
})
{
final
List
<
_DiagnosticsPathNode
>
path
=
<
_DiagnosticsPathNode
>[];
if
(
chain
.
isEmpty
)
return
path
;
DiagnosticsNode
diagnostic
=
chain
.
first
.
toDiagnosticsNode
(
name:
name
,
style:
style
);
for
(
int
i
=
1
;
i
<
chain
.
length
;
i
+=
1
)
{
final
Diagnosticable
target
=
chain
[
i
];
bool
foundMatch
=
false
;
final
List
<
DiagnosticsNode
>
children
=
diagnostic
.
getChildren
();
for
(
int
j
=
0
;
j
<
children
.
length
;
j
+=
1
)
{
final
DiagnosticsNode
child
=
children
[
j
];
if
(
child
.
value
==
target
)
{
foundMatch
=
true
;
path
.
add
(
new
_DiagnosticsPathNode
(
node:
diagnostic
,
children:
children
,
childIndex:
j
,
));
diagnostic
=
child
;
break
;
}
}
assert
(
foundMatch
);
}
path
.
add
(
new
_DiagnosticsPathNode
(
node:
diagnostic
,
children:
diagnostic
.
getChildren
()));
return
path
;
}
/// Signature for the selection change callback used by
/// [WidgetInspectorService.selectionChangedCallback].
typedef
void
InspectorSelectionChangedCallback
(
);
/// Structure to help reference count Dart objects referenced by a GUI tool
/// using [WidgetInspectorService].
class
_InspectorReferenceData
{
_InspectorReferenceData
(
this
.
object
);
final
Object
object
;
int
count
=
1
;
}
/// Service used by GUI tools to interact with the [WidgetInspector].
///
/// Calls to this object are typically made from GUI tools such as the [Flutter
/// IntelliJ Plugin](https://github.com/flutter/flutter-intellij/blob/master/README.md)
/// using the [Dart VM Service protocol](https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md).
/// This class uses its own object id and manages object lifecycles itself
/// instead of depending on the [object ids](https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#getobject)
/// specified by the VM Service Protocol because the VM Service Protocol ids
/// expire unpredictably. Object references are tracked in groups so that tools
/// that clients can use dereference all objects in a group with a single
/// operation making it easier to avoid memory leaks.
///
/// All methods in this class are appropriate to invoke from debugging tools
/// using the Observatory service protocol to evaluate Dart expressions of the
/// form `WidgetInspectorService.instance.methodName(arg1, arg2, ...)`. If you
/// make changes to any instance method of this class you need to verify that
/// the [Flutter IntelliJ Plugin](https://github.com/flutter/flutter-intellij/blob/master/README.md)
/// widget inspector support still works with the changes.
///
/// All methods returning String values return JSON.
class
WidgetInspectorService
{
WidgetInspectorService
.
_
();
/// The current [WidgetInspectorService].
static
WidgetInspectorService
get
instance
=>
_instance
;
static
final
WidgetInspectorService
_instance
=
new
WidgetInspectorService
.
_
();
/// Ground truth tracking what object(s) are currently selected used by both
/// GUI tools such as the Flutter IntelliJ Plugin and the [WidgetInspector]
/// displayed on the device.
final
InspectorSelection
selection
=
new
InspectorSelection
();
/// Callback typically registered by the [WidgetInspector] to receive
/// notifications when [selection] changes.
///
/// The Flutter IntelliJ Plugin does not need to listen for this event as it
/// instead listens for `dart:developer` `inspect` events which also trigger
/// when the inspection target changes on device.
InspectorSelectionChangedCallback
selectionChangedCallback
;
/// The Observatory protocol does not keep alive object references so this
/// class needs to manually manage groups of objects that should be kept
/// alive.
final
Map
<
String
,
Set
<
_InspectorReferenceData
>>
_groups
=
<
String
,
Set
<
_InspectorReferenceData
>>{};
final
Map
<
String
,
_InspectorReferenceData
>
_idToReferenceData
=
<
String
,
_InspectorReferenceData
>{};
final
Map
<
Object
,
String
>
_objectToId
=
new
Map
<
Object
,
String
>.
identity
();
int
_nextId
=
0
;
/// Clear all InspectorService object references.
///
/// Use this method only for testing to ensure that object references from one
/// test case do not impact other test cases.
void
disposeAllGroups
()
{
_groups
.
clear
();
_idToReferenceData
.
clear
();
_objectToId
.
clear
();
_nextId
=
0
;
}
/// Free all references to objects in a group.
///
/// Objects and their associated ids in the group may be kept alive by
/// references from a different group.
void
disposeGroup
(
String
name
)
{
final
Set
<
_InspectorReferenceData
>
references
=
_groups
.
remove
(
name
);
if
(
references
==
null
)
return
;
references
.
forEach
(
_decrementReferenceCount
);
}
void
_decrementReferenceCount
(
_InspectorReferenceData
reference
)
{
reference
.
count
-=
1
;
assert
(
reference
.
count
>=
0
);
if
(
reference
.
count
==
0
)
{
final
String
id
=
_objectToId
.
remove
(
reference
.
object
);
assert
(
id
!=
null
);
_idToReferenceData
.
remove
(
id
);
}
}
/// Returns a unique id for [object] that will remain live at least until
/// [disposeGroup] is called on [groupName] or [dispose] is called on the id
/// returned by this method.
String
toId
(
Object
object
,
String
groupName
)
{
if
(
object
==
null
)
return
null
;
final
Set
<
_InspectorReferenceData
>
group
=
_groups
.
putIfAbsent
(
groupName
,
()
=>
new
Set
<
_InspectorReferenceData
>.
identity
());
String
id
=
_objectToId
[
object
];
_InspectorReferenceData
referenceData
;
if
(
id
==
null
)
{
id
=
'inspector-
$_nextId
'
;
_nextId
+=
1
;
_objectToId
[
object
]
=
id
;
referenceData
=
new
_InspectorReferenceData
(
object
);
_idToReferenceData
[
id
]
=
referenceData
;
group
.
add
(
referenceData
);
}
else
{
referenceData
=
_idToReferenceData
[
id
];
if
(
group
.
add
(
referenceData
))
referenceData
.
count
+=
1
;
}
return
id
;
}
/// Returns the Dart object associated with a reference id.
///
/// The `groupName` parameter is not required by is added to regularize the
/// API surface of the methods in this class called from the Flutter IntelliJ
/// Plugin.
Object
toObject
(
String
id
,
[
String
groupName
])
{
if
(
id
==
null
)
return
null
;
final
_InspectorReferenceData
data
=
_idToReferenceData
[
id
];
if
(
data
==
null
)
throw
new
FlutterError
(
'Id does not exist'
);
return
data
.
object
;
}
/// Returns the object to introspect to determine the source location of an
/// object's class.
///
/// The Dart object for the id is returned for all cases but [Element] objects
/// where the [Widget] configuring the [Element] is returned instead as the
/// class of the [Widget] is more relevant than the class of the [Element].
///
/// The `groupName` parameter is not required by is added to regularize the
/// API surface of methods called from the Flutter IntelliJ Plugin.
Object
toObjectForSourceLocation
(
String
id
,
[
String
groupName
])
{
final
Object
object
=
toObject
(
id
);
if
(
object
is
Element
)
{
return
object
.
widget
;
}
return
object
;
}
/// Remove the object with the specified `id` from the specified object
/// group.
///
/// If the object exists in other groups it will remain alive and the object
/// id will remain valid.
void
disposeId
(
String
id
,
String
groupName
)
{
if
(
id
==
null
)
return
;
final
_InspectorReferenceData
referenceData
=
_idToReferenceData
[
id
];
if
(
referenceData
==
null
)
throw
new
FlutterError
(
'Id does not exist'
);
if
(
_groups
[
groupName
]?.
remove
(
referenceData
)
!=
true
)
throw
new
FlutterError
(
'Id is not in group'
);
_decrementReferenceCount
(
referenceData
);
}
/// Set the [WidgetInspector] selection to the object matching the specified
/// id if the object is valid object to set as the inspector selection.
///
/// Returns `true` if the selection was changed.
///
/// The `groupName` parameter is not required by is added to regularize the
/// API surface of methods called from the Flutter IntelliJ Plugin.
bool
setSelectionById
(
String
id
,
[
String
groupName
])
{
return
setSelection
(
toObject
(
id
),
groupName
);
}
/// Set the [WidgetInspector] selection to the specified `object` if it is
/// a valid object to set as the inspector selection.
///
/// Returns `true` if the selection was changed.
///
/// The `groupName` parameter is not needed but is specified to regularize the
/// API surface of methods called from the Flutter IntelliJ Plugin.
bool
setSelection
(
Object
object
,
[
String
groupName
])
{
if
(
object
is
Element
||
object
is
RenderObject
)
{
if
(
object
is
Element
)
{
if
(
object
==
selection
.
currentElement
)
{
return
false
;
}
selection
.
currentElement
=
object
;
}
else
{
if
(
object
==
selection
.
current
)
{
return
false
;
}
selection
.
current
=
object
;
}
if
(
selectionChangedCallback
!=
null
)
{
selectionChangedCallback
();
}
return
true
;
}
return
false
;
}
/// Returns JSON representing the chain of [DiagnosticsNode] instances from
/// root of thee tree to the [Element] or [RenderObject] matching `id`.
///
/// The JSON contains all information required to display a tree view with
/// all nodes other than nodes along the path collapsed.
String
getParentChain
(
String
id
,
String
groupName
)
{
final
Object
value
=
toObject
(
id
);
List
<
_DiagnosticsPathNode
>
path
;
if
(
value
is
RenderObject
)
path
=
_getRenderObjectParentChain
(
value
,
groupName
);
else
if
(
value
is
Element
)
path
=
_getElementParentChain
(
value
,
groupName
);
else
throw
new
FlutterError
(
'Cannot get parent chain for node of type
${value.runtimeType}
'
);
return
JSON
.
encode
(
path
.
map
((
_DiagnosticsPathNode
node
)
=>
_pathNodeToJson
(
node
,
groupName
)).
toList
());
}
Map
<
String
,
Object
>
_pathNodeToJson
(
_DiagnosticsPathNode
pathNode
,
String
groupName
)
{
if
(
pathNode
==
null
)
return
null
;
return
<
String
,
Object
>{
'node'
:
_nodeToJson
(
pathNode
.
node
,
groupName
),
'children'
:
_nodesToJson
(
pathNode
.
children
,
groupName
),
'childIndex'
:
pathNode
.
childIndex
,
};
}
List
<
_DiagnosticsPathNode
>
_getElementParentChain
(
Element
element
,
String
groupName
)
{
return
_followDiagnosticableChain
(
element
?.
debugGetDiagnosticChain
()?.
reversed
?.
toList
())
??
const
<
_DiagnosticsPathNode
>[];
}
List
<
_DiagnosticsPathNode
>
_getRenderObjectParentChain
(
RenderObject
renderObject
,
String
groupName
)
{
final
List
<
RenderObject
>
chain
=
<
RenderObject
>[];
while
(
renderObject
!=
null
)
{
chain
.
add
(
renderObject
);
renderObject
=
renderObject
.
parent
;
}
return
_followDiagnosticableChain
(
chain
.
reversed
.
toList
());
}
Map
<
String
,
Object
>
_nodeToJson
(
DiagnosticsNode
node
,
String
groupName
)
{
if
(
node
==
null
)
return
null
;
final
Map
<
String
,
Object
>
json
=
node
.
toJsonMap
();
json
[
'objectId'
]
=
toId
(
node
,
groupName
);
json
[
'valueId'
]
=
toId
(
node
.
value
,
groupName
);
return
json
;
}
String
_serialize
(
DiagnosticsNode
node
,
String
groupName
)
{
return
JSON
.
encode
(
_nodeToJson
(
node
,
groupName
));
}
List
<
Map
<
String
,
Object
>>
_nodesToJson
(
Iterable
<
DiagnosticsNode
>
nodes
,
String
groupName
)
{
if
(
nodes
==
null
)
return
<
Map
<
String
,
Object
>>[];
return
nodes
.
map
<
Map
<
String
,
Object
>>((
DiagnosticsNode
node
)
=>
_nodeToJson
(
node
,
groupName
)).
toList
();
}
/// Returns a JSON representation of the properties of the [DiagnosticsNode]
/// object that `diagnosticsNodeId` references.
String
getProperties
(
String
diagnosticsNodeId
,
String
groupName
)
{
final
DiagnosticsNode
node
=
toObject
(
diagnosticsNodeId
);
return
JSON
.
encode
(
_nodesToJson
(
node
==
null
?
const
<
DiagnosticsNode
>[]
:
node
.
getProperties
(),
groupName
));
}
/// Returns a JSON representation of the children of the [DiagnosticsNode]
/// object that `diagnosticsNodeId` references.
String
getChildren
(
String
diagnosticsNodeId
,
String
groupName
)
{
final
DiagnosticsNode
node
=
toObject
(
diagnosticsNodeId
);
return
JSON
.
encode
(
_nodesToJson
(
node
==
null
?
const
<
DiagnosticsNode
>[]
:
node
.
getChildren
(),
groupName
));
}
/// Returns a JSON representation of the [DiagnosticsNode] for the root
/// [Element].
String
getRootWidget
(
String
groupName
)
{
return
_serialize
(
WidgetsBinding
.
instance
?.
renderViewElement
?.
toDiagnosticsNode
(),
groupName
);
}
/// Returns a JSON representation of the [DiagnosticsNode] for the root
/// [RenderObject].
String
getRootRenderObject
(
String
groupName
)
{
return
_serialize
(
RendererBinding
.
instance
?.
renderView
?.
toDiagnosticsNode
(),
groupName
);
}
/// Returns a [DiagnosticsNode] representing the currently selected
/// [RenderObject].
///
/// If the currently selected [RenderObject] is identical to the
/// [RenderObject] referenced by `previousSelectionId` then the previous
/// [DiagnosticNode] is reused.
String
getSelectedRenderObject
(
String
previousSelectionId
,
String
groupName
)
{
final
DiagnosticsNode
previousSelection
=
toObject
(
previousSelectionId
);
final
RenderObject
current
=
selection
?.
current
;
return
_serialize
(
current
==
previousSelection
?.
value
?
previousSelection
:
current
?.
toDiagnosticsNode
(),
groupName
);
}
/// Returns a [DiagnosticsNode] representing the currently selected [Element].
///
/// If the currently selected [Element] is identical to the [Element]
/// referenced by `previousSelectionId` then the previous [DiagnosticNode] is
/// reused.
String
getSelectedWidget
(
String
previousSelectionId
,
String
groupName
)
{
final
DiagnosticsNode
previousSelection
=
toObject
(
previousSelectionId
);
final
Element
current
=
selection
?.
currentElement
;
return
_serialize
(
current
==
previousSelection
?.
value
?
previousSelection
:
current
?.
toDiagnosticsNode
(),
groupName
);
}
}
/// A widget that enables inspecting the child widget's structure.
///
/// Select a location on your device or emulator and view what widgets and
...
...
@@ -70,9 +448,11 @@ class WidgetInspector extends StatefulWidget {
class
_WidgetInspectorState
extends
State
<
WidgetInspector
>
with
WidgetsBindingObserver
{
_WidgetInspectorState
()
:
selection
=
WidgetInspectorService
.
instance
.
selection
;
Offset
_lastPointerLocation
;
final
InspectorSelection
selection
=
new
InspectorSelection
()
;
final
InspectorSelection
selection
;
/// Whether the inspector is in select mode.
///
...
...
@@ -87,6 +467,17 @@ class _WidgetInspectorState extends State<WidgetInspector>
/// as selecting the edge of the bounding box.
static
const
double
_kEdgeHitMargin
=
2.0
;
@override
void
initState
()
{
super
.
initState
();
WidgetInspectorService
.
instance
.
selectionChangedCallback
=
()
{
setState
(()
{
// The [selection] property which the build method depends on has
// changed.
});
};
}
bool
_hitTestHelper
(
List
<
RenderObject
>
hits
,
List
<
RenderObject
>
edgeHits
,
...
...
@@ -252,30 +643,66 @@ class InspectorSelection {
List
<
RenderObject
>
_candidates
=
<
RenderObject
>[];
set
candidates
(
List
<
RenderObject
>
value
)
{
_candidates
=
value
;
index
=
0
;
_index
=
0
;
_computeCurrent
();
}
/// Index within the list of candidates that is currently selected.
int
index
=
0
;
int
get
index
=>
_index
;
int
_index
=
0
;
set
index
(
int
value
)
{
_index
=
value
;
_computeCurrent
();
}
/// Set the selection to empty.
void
clear
()
{
_candidates
=
<
RenderObject
>[];
index
=
0
;
_index
=
0
;
_computeCurrent
();
}
/// Selected render object typically from the [candidates] list.
///
/// Setting [candidates] or calling [clear] resets the selection.
///
/// Returns null if the selection is invalid.
RenderObject
get
current
=>
_current
;
RenderObject
_current
;
set
current
(
RenderObject
value
)
{
if
(
_current
!=
value
)
{
_current
=
value
;
_currentElement
=
value
.
debugCreator
.
element
;
}
}
/// Selected
render object from the [candidates] list
.
/// Selected
[Element] consistent with the [current] selected [RenderObject]
.
///
/// Setting [candidates] or calling [clear] resets the selection.
///
/// Returns null if the selection is invalid.
RenderObject
get
current
{
return
candidates
!=
null
&&
index
<
candidates
.
length
?
candidates
[
index
]
:
null
;
Element
get
currentElement
=>
_currentElement
;
Element
_currentElement
;
set
currentElement
(
Element
element
)
{
if
(
currentElement
!=
element
)
{
_currentElement
=
element
;
_current
=
element
.
findRenderObject
();
}
}
void
_computeCurrent
()
{
if
(
_index
<
candidates
.
length
)
{
_current
=
candidates
[
index
];
_currentElement
=
_current
.
debugCreator
.
element
;
}
else
{
_current
=
null
;
_currentElement
=
null
;
}
}
/// Whether the selected render object is attached to the tree or has gone
/// out of scope.
bool
get
active
=>
current
!=
null
&&
current
.
attached
;
bool
get
active
=>
_current
!=
null
&&
_
current
.
attached
;
}
class
_InspectorOverlay
extends
LeafRenderObjectWidget
{
...
...
@@ -448,7 +875,7 @@ class _InspectorOverlayLayer extends Layer {
final
_InspectorOverlayRenderState
state
=
new
_InspectorOverlayRenderState
(
overlayRect:
overlayRect
,
selected:
new
_TransformedRect
(
selected
),
tooltip:
selected
.
toString
(),
tooltip:
selection
.
currentElement
.
toStringShort
(),
textDirection:
TextDirection
.
ltr
,
candidates:
candidates
,
);
...
...
packages/flutter/test/foundation/diagnostics_test.dart
View file @
277001d1
...
...
@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:convert'
;
import
'dart:ui'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
...
@@ -46,6 +48,136 @@ enum ExampleEnum {
deferToChild
,
}
/// Encode and decode to JSON to make sure all objects in the JSON for the
/// [DiagnosticsNode] are valid JSON.
Map
<
String
,
Object
>
simulateJsonSerialization
(
DiagnosticsNode
node
)
{
return
JSON
.
decode
(
JSON
.
encode
(
node
.
toJsonMap
()));
}
void
validateNodeJsonSerialization
(
DiagnosticsNode
node
)
{
validateNodeJsonSerializationHelper
(
simulateJsonSerialization
(
node
),
node
);
}
void
validateNodeJsonSerializationHelper
(
Map
<
String
,
Object
>
json
,
DiagnosticsNode
node
)
{
expect
(
json
[
'name'
],
equals
(
node
.
name
));
expect
(
json
[
'showSeparator'
],
equals
(
node
.
showSeparator
));
expect
(
json
[
'description'
],
equals
(
node
.
toDescription
()));
expect
(
json
[
'level'
],
equals
(
describeEnum
(
node
.
level
)));
expect
(
json
[
'showName'
],
equals
(
node
.
showName
));
expect
(
json
[
'emptyBodyDescription'
],
equals
(
node
.
emptyBodyDescription
));
expect
(
json
[
'style'
],
equals
(
describeEnum
(
node
.
style
)));
final
String
valueToString
=
node
is
DiagnosticsProperty
?
node
.
valueToString
()
:
node
.
value
.
toString
();
expect
(
json
[
'valueToString'
],
equals
(
valueToString
));
expect
(
json
[
'type'
],
equals
(
node
.
runtimeType
.
toString
()));
expect
(
json
[
'hasChildren'
],
equals
(
node
.
getChildren
().
isNotEmpty
));
}
void
validatePropertyJsonSerialization
(
DiagnosticsProperty
<
Object
>
property
)
{
validatePropertyJsonSerializationHelper
(
simulateJsonSerialization
(
property
),
property
);
}
void
validateStringPropertyJsonSerialization
(
StringProperty
property
)
{
final
Map
<
String
,
Object
>
json
=
simulateJsonSerialization
(
property
);
expect
(
json
[
'quoted'
],
equals
(
property
.
quoted
));
validatePropertyJsonSerializationHelper
(
json
,
property
);
}
void
validateFlagPropertyJsonSerialization
(
FlagProperty
property
)
{
final
Map
<
String
,
Object
>
json
=
simulateJsonSerialization
(
property
);
expect
(
json
[
'ifTrue'
],
equals
(
property
.
ifTrue
));
if
(
property
.
ifTrue
!=
null
)
{
expect
(
json
[
'ifTrue'
],
equals
(
property
.
ifTrue
));
}
else
{
expect
(
json
.
containsKey
(
'ifTrue'
),
isFalse
);
}
if
(
property
.
ifFalse
!=
null
)
{
expect
(
json
[
'ifFalse'
],
property
.
ifFalse
);
}
else
{
expect
(
json
.
containsKey
(
'isFalse'
),
isFalse
);
}
validatePropertyJsonSerializationHelper
(
json
,
property
);
}
void
validateDoublePropertyJsonSerialization
(
DoubleProperty
property
)
{
final
Map
<
String
,
Object
>
json
=
simulateJsonSerialization
(
property
);
if
(
property
.
unit
!=
null
)
{
expect
(
json
[
'unit'
],
equals
(
property
.
unit
));
}
else
{
expect
(
json
.
containsKey
(
'unit'
),
isFalse
);
}
expect
(
json
[
'numberToString'
],
equals
(
property
.
numberToString
()));
validatePropertyJsonSerializationHelper
(
json
,
property
);
}
void
validateObjectFlagPropertyJsonSerialization
(
ObjectFlagProperty
<
Object
>
property
)
{
final
Map
<
String
,
Object
>
json
=
simulateJsonSerialization
(
property
);
if
(
property
.
ifPresent
!=
null
)
{
expect
(
json
[
'ifPresent'
],
equals
(
property
.
ifPresent
));
}
else
{
expect
(
json
.
containsKey
(
'ifPresent'
),
isFalse
);
}
validatePropertyJsonSerializationHelper
(
json
,
property
);
}
void
validateIterablePropertyJsonSerialization
(
IterableProperty
<
Object
>
property
)
{
final
Map
<
String
,
Object
>
json
=
simulateJsonSerialization
(
property
);
if
(
property
.
value
!=
null
)
{
final
List
<
Object
>
valuesJson
=
json
[
'values'
];
final
List
<
String
>
expectedValues
=
property
.
value
.
map
<
String
>((
Object
value
)
=>
value
.
toString
()).
toList
();
expect
(
listEquals
(
valuesJson
,
expectedValues
),
isTrue
);
}
else
{
expect
(
json
.
containsKey
(
'values'
),
isFalse
);
}
validatePropertyJsonSerializationHelper
(
json
,
property
);
}
void
validatePropertyJsonSerializationHelper
(
final
Map
<
String
,
Object
>
json
,
DiagnosticsProperty
<
Object
>
property
)
{
if
(
property
.
defaultValue
!=
kNoDefaultValue
)
{
expect
(
json
[
'defaultValue'
],
equals
(
property
.
defaultValue
.
toString
()));
}
else
{
expect
(
json
.
containsKey
(
'defaultValue'
),
isFalse
);
}
if
(
property
.
ifEmpty
!=
null
)
{
expect
(
json
[
'ifEmpty'
],
equals
(
property
.
ifEmpty
));
}
else
{
expect
(
json
.
containsKey
(
'ifEmpty'
),
isFalse
);
}
if
(
property
.
ifNull
!=
null
)
{
expect
(
json
[
'ifNull'
],
equals
(
property
.
ifNull
));
}
else
{
expect
(
json
.
containsKey
(
'ifNull'
),
isFalse
);
}
if
(
property
.
tooltip
!=
null
)
{
expect
(
json
[
'tooltip'
],
equals
(
property
.
tooltip
));
}
else
{
expect
(
json
.
containsKey
(
'tooltip'
),
isFalse
);
}
expect
(
json
[
'missingIfNull'
],
equals
(
property
.
missingIfNull
));
if
(
property
.
exception
!=
null
)
{
expect
(
json
[
'exception'
],
equals
(
property
.
exception
.
toString
()));
}
else
{
expect
(
json
.
containsKey
(
'exception'
),
isFalse
);
}
expect
(
json
[
'propertyType'
],
equals
(
property
.
propertyType
.
toString
()));
expect
(
json
[
'valueToString'
],
equals
(
property
.
valueToString
()));
expect
(
json
.
containsKey
(
'defaultLevel'
),
isTrue
);
if
(
property
.
value
is
Diagnosticable
)
{
expect
(
json
[
'isDiagnosticableValue'
],
isTrue
);
}
else
{
expect
(
json
.
containsKey
(
'isDiagnosticableValue'
),
isFalse
);
}
validateNodeJsonSerializationHelper
(
json
,
property
);
}
void
main
(
)
{
test
(
'TreeDiagnosticsMixin control test'
,
()
async
{
void
goldenStyleTest
(
String
description
,
...
...
@@ -72,6 +204,7 @@ void main() {
equalsIgnoringHashCodes
(
golden
),
reason:
description
,
);
validateNodeJsonSerialization
(
tree
.
toDiagnosticsNode
());
}
goldenStyleTest
(
...
...
@@ -242,6 +375,7 @@ void main() {
equalsIgnoringHashCodes
(
golden
),
reason:
description
,
);
validateNodeJsonSerialization
(
tree
.
toDiagnosticsNode
());
}
goldenStyleTest
(
...
...
@@ -552,16 +686,15 @@ void main() {
equals
(
'name: value'
),
);
expect
(
new
StringProperty
(
'name'
,
'value'
,
description:
'VALUE'
,
ifEmpty:
'<hidden>'
,
quoted:
false
,
).
toString
(),
equals
(
'name: VALUE'
),
final
StringProperty
stringProperty
=
new
StringProperty
(
'name'
,
'value'
,
description:
'VALUE'
,
ifEmpty:
'<hidden>'
,
quoted:
false
,
);
expect
(
stringProperty
.
toString
(),
equals
(
'name: VALUE'
));
validateStringPropertyJsonSerialization
(
stringProperty
);
expect
(
new
StringProperty
(
...
...
@@ -592,14 +725,13 @@ void main() {
expect
(
new
StringProperty
(
'name'
,
null
).
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
new
StringProperty
(
'name'
,
'value'
,
level:
DiagnosticLevel
.
hidden
).
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
new
StringProperty
(
'name'
,
null
,
defaultValue:
null
).
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
new
StringProperty
(
'name'
,
'value'
,
quoted:
true
,
).
toString
(),
equals
(
'name: "value"'
),
final
StringProperty
quoted
=
new
StringProperty
(
'name'
,
'value'
,
quoted:
true
,
);
expect
(
quoted
.
toString
(),
equals
(
'name: "value"'
));
validateStringPropertyJsonSerialization
(
quoted
);
expect
(
new
StringProperty
(
'name'
,
'value'
,
showName:
false
).
toString
(),
...
...
@@ -626,14 +758,18 @@ void main() {
expect
(
falseProperty
.
toString
(),
equals
(
'name: false'
));
expect
(
falseProperty
.
value
,
isFalse
);
expect
(
falseProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
validatePropertyJsonSerialization
(
trueProperty
);
validatePropertyJsonSerialization
(
falseProperty
);
final
DiagnosticsProperty
<
bool
>
truthyProperty
=
new
DiagnosticsProperty
<
bool
>(
'name'
,
true
,
description:
'truthy'
,
);
expect
(
new
DiagnosticsProperty
<
bool
>(
'name'
,
true
,
description:
'truthy'
,
).
toString
(),
truthyProperty
.
toString
(),
equals
(
'name: truthy'
),
);
validatePropertyJsonSerialization
(
truthyProperty
);
expect
(
new
DiagnosticsProperty
<
bool
>(
'name'
,
true
,
showName:
false
).
toString
(),
equals
(
'true'
),
...
...
@@ -642,10 +778,12 @@ void main() {
expect
(
new
DiagnosticsProperty
<
bool
>(
'name'
,
null
).
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
new
DiagnosticsProperty
<
bool
>(
'name'
,
true
,
level:
DiagnosticLevel
.
hidden
).
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
new
DiagnosticsProperty
<
bool
>(
'name'
,
null
,
defaultValue:
null
).
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
final
DiagnosticsProperty
<
bool
>
missingBool
=
new
DiagnosticsProperty
<
bool
>(
'name'
,
null
,
ifNull:
'missing'
);
expect
(
new
DiagnosticsProperty
<
bool
>(
'name'
,
null
,
ifNull:
'missing'
)
.
toString
(),
missingBool
.
toString
(),
equals
(
'name: missing'
),
);
validatePropertyJsonSerialization
(
missingBool
);
});
test
(
'flag property test'
,
()
{
...
...
@@ -660,6 +798,8 @@ void main() {
ifTrue:
'myFlag'
,
);
expect
(
trueFlag
.
toString
(),
equals
(
'myFlag'
));
validateFlagPropertyJsonSerialization
(
trueFlag
);
validateFlagPropertyJsonSerialization
(
falseFlag
);
expect
(
trueFlag
.
value
,
isTrue
);
expect
(
falseFlag
.
value
,
isFalse
);
...
...
@@ -680,6 +820,7 @@ void main() {
);
expect
(
withTooltip
.
value
,
equals
(
'value'
));
expect
(
withTooltip
.
isFiltered
(
DiagnosticLevel
.
fine
),
isFalse
);
validatePropertyJsonSerialization
(
withTooltip
);
});
test
(
'double property test'
,
()
{
...
...
@@ -690,6 +831,7 @@ void main() {
expect
(
doubleProperty
.
toString
(),
equals
(
'name: 42.0'
));
expect
(
doubleProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
doubleProperty
.
value
,
equals
(
42.0
));
validateDoublePropertyJsonSerialization
(
doubleProperty
);
expect
(
new
DoubleProperty
(
'name'
,
1.3333
).
toString
(),
equals
(
'name: 1.3'
));
...
...
@@ -701,11 +843,9 @@ void main() {
equals
(
'name: missing'
),
);
expect
(
new
DoubleProperty
(
'name'
,
42.0
,
unit:
'px'
,
).
toString
(),
equals
(
'name: 42.0px'
),
);
final
DoubleProperty
doubleWithUnit
=
new
DoubleProperty
(
'name'
,
42.0
,
unit:
'px'
);
expect
(
doubleWithUnit
.
toString
(),
equals
(
'name: 42.0px'
));
validateDoublePropertyJsonSerialization
(
doubleWithUnit
);
});
...
...
@@ -717,7 +857,7 @@ void main() {
expect
(
safe
.
toString
(),
equals
(
'name: 42.0'
));
expect
(
safe
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
safe
.
value
,
equals
(
42.0
));
validateDoublePropertyJsonSerialization
(
safe
);
expect
(
new
DoubleProperty
.
lazy
(
'name'
,
()
=>
1.3333
).
toString
(),
equals
(
'name: 1.3'
),
...
...
@@ -745,6 +885,7 @@ void main() {
equals
(
'name: EXCEPTION (FlutterError)'
),
);
expect
(
throwingProperty
.
level
,
equals
(
DiagnosticLevel
.
error
));
validateDoublePropertyJsonSerialization
(
throwingProperty
);
});
test
(
'percent property'
,
()
{
...
...
@@ -753,10 +894,12 @@ void main() {
equals
(
'name: 40.0%'
),
);
final
PercentProperty
complexPercentProperty
=
new
PercentProperty
(
'name'
,
0.99
,
unit:
'invisible'
,
tooltip:
'almost transparent'
);
expect
(
new
PercentProperty
(
'name'
,
0.99
,
unit:
'invisible'
,
tooltip:
'almost transparent'
)
.
toString
(),
complexPercentProperty
.
toString
(),
equals
(
'name: 99.0% invisible (almost transparent)'
),
);
validateDoublePropertyJsonSerialization
(
complexPercentProperty
);
expect
(
new
PercentProperty
(
'name'
,
null
,
unit:
'invisible'
,
tooltip:
'!'
).
toString
(),
...
...
@@ -830,8 +973,10 @@ void main() {
expect
(
present
.
toString
(),
equals
(
'clickable'
));
expect
(
present
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
present
.
value
,
equals
(
onClick
));
validateObjectFlagPropertyJsonSerialization
(
present
);
expect
(
missing
.
toString
(),
equals
(
'onClick: null'
));
expect
(
missing
.
isFiltered
(
DiagnosticLevel
.
fine
),
isTrue
);
validateObjectFlagPropertyJsonSerialization
(
missing
);
});
test
(
'missing callback property test'
,
()
{
...
...
@@ -852,6 +997,8 @@ void main() {
expect
(
present
.
value
,
equals
(
onClick
));
expect
(
missing
.
toString
(),
equals
(
'disabled'
));
expect
(
missing
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
validateObjectFlagPropertyJsonSerialization
(
present
);
validateObjectFlagPropertyJsonSerialization
(
missing
);
});
test
(
'describe bool property'
,
()
{
...
...
@@ -872,9 +1019,11 @@ void main() {
expect
(
yes
.
toString
(),
equals
(
'name: YES'
));
expect
(
yes
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
yes
.
value
,
isTrue
);
validateFlagPropertyJsonSerialization
(
yes
);
expect
(
no
.
toString
(),
equals
(
'name: NO'
));
expect
(
no
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
no
.
value
,
isFalse
);
validateFlagPropertyJsonSerialization
(
no
);
expect
(
new
FlagProperty
(
...
...
@@ -929,18 +1078,22 @@ void main() {
expect
(
hello
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
hello
.
value
,
equals
(
ExampleEnum
.
hello
));
expect
(
hello
.
toString
(),
equals
(
'name: hello'
));
validatePropertyJsonSerialization
(
hello
);
expect
(
world
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
world
.
value
,
equals
(
ExampleEnum
.
world
));
expect
(
world
.
toString
(),
equals
(
'name: world'
));
validatePropertyJsonSerialization
(
world
);
expect
(
deferToChild
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
deferToChild
.
value
,
equals
(
ExampleEnum
.
deferToChild
));
expect
(
deferToChild
.
toString
(),
equals
(
'name: defer-to-child'
));
validatePropertyJsonSerialization
(
deferToChild
);
expect
(
nullEnum
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
nullEnum
.
value
,
isNull
);
expect
(
nullEnum
.
toString
(),
equals
(
'name: null'
));
validatePropertyJsonSerialization
(
nullEnum
);
final
EnumProperty
<
ExampleEnum
>
matchesDefault
=
new
EnumProperty
<
ExampleEnum
>(
'name'
,
...
...
@@ -950,7 +1103,7 @@ void main() {
expect
(
matchesDefault
.
toString
(),
equals
(
'name: hello'
));
expect
(
matchesDefault
.
value
,
equals
(
ExampleEnum
.
hello
));
expect
(
matchesDefault
.
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
validatePropertyJsonSerialization
(
matchesDefault
);
expect
(
new
EnumProperty
<
ExampleEnum
>(
...
...
@@ -1052,6 +1205,7 @@ void main() {
expect
(
simple
.
value
,
equals
(
rect
));
expect
(
simple
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
simple
.
toString
(),
equals
(
'name: Rect.fromLTRB(0.0, 0.0, 20.0, 20.0)'
));
validatePropertyJsonSerialization
(
simple
);
final
DiagnosticsNode
withDescription
=
new
DiagnosticsProperty
<
Rect
>(
'name'
,
...
...
@@ -1061,6 +1215,7 @@ void main() {
expect
(
withDescription
.
value
,
equals
(
rect
));
expect
(
withDescription
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
withDescription
.
toString
(),
equals
(
'name: small rect'
));
validatePropertyJsonSerialization
(
withDescription
);
final
DiagnosticsProperty
<
Object
>
nullProperty
=
new
DiagnosticsProperty
<
Object
>(
'name'
,
...
...
@@ -1069,6 +1224,7 @@ void main() {
expect
(
nullProperty
.
value
,
isNull
);
expect
(
nullProperty
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
nullProperty
.
toString
(),
equals
(
'name: null'
));
validatePropertyJsonSerialization
(
nullProperty
);
final
DiagnosticsProperty
<
Object
>
hideNullProperty
=
new
DiagnosticsProperty
<
Object
>(
'name'
,
...
...
@@ -1078,6 +1234,7 @@ void main() {
expect
(
hideNullProperty
.
value
,
isNull
);
expect
(
hideNullProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
hideNullProperty
.
toString
(),
equals
(
'name: null'
));
validatePropertyJsonSerialization
(
hideNullProperty
);
final
DiagnosticsNode
nullDescription
=
new
DiagnosticsProperty
<
Object
>(
'name'
,
...
...
@@ -1087,6 +1244,7 @@ void main() {
expect
(
nullDescription
.
value
,
isNull
);
expect
(
nullDescription
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
nullDescription
.
toString
(),
equals
(
'name: missing'
));
validatePropertyJsonSerialization
(
nullDescription
);
final
DiagnosticsProperty
<
Rect
>
hideName
=
new
DiagnosticsProperty
<
Rect
>(
'name'
,
...
...
@@ -1097,6 +1255,7 @@ void main() {
expect
(
hideName
.
value
,
equals
(
rect
));
expect
(
hideName
.
level
,
equals
(
DiagnosticLevel
.
warning
));
expect
(
hideName
.
toString
(),
equals
(
'Rect.fromLTRB(0.0, 0.0, 20.0, 20.0)'
));
validatePropertyJsonSerialization
(
hideName
);
final
DiagnosticsProperty
<
Rect
>
hideSeparator
=
new
DiagnosticsProperty
<
Rect
>(
'Creator'
,
...
...
@@ -1109,6 +1268,7 @@ void main() {
hideSeparator
.
toString
(),
equals
(
'Creator Rect.fromLTRB(0.0, 0.0, 20.0, 20.0)'
),
);
validatePropertyJsonSerialization
(
hideSeparator
);
});
test
(
'lazy object property test'
,
()
{
...
...
@@ -1121,8 +1281,9 @@ void main() {
expect
(
simple
.
value
,
equals
(
rect
));
expect
(
simple
.
level
,
equals
(
DiagnosticLevel
.
info
));
expect
(
simple
.
toString
(),
equals
(
'name: small rect'
));
validatePropertyJsonSerialization
(
simple
);
final
Diagnostics
Node
nullProperty
=
new
DiagnosticsProperty
<
Object
>.
lazy
(
final
Diagnostics
Property
<
Object
>
nullProperty
=
new
DiagnosticsProperty
<
Object
>.
lazy
(
'name'
,
()
=>
null
,
description:
'missing'
,
...
...
@@ -1130,6 +1291,7 @@ void main() {
expect
(
nullProperty
.
value
,
isNull
);
expect
(
nullProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
nullProperty
.
toString
(),
equals
(
'name: missing'
));
validatePropertyJsonSerialization
(
nullProperty
);
final
DiagnosticsNode
hideNullProperty
=
new
DiagnosticsProperty
<
Object
>.
lazy
(
'name'
,
...
...
@@ -1140,6 +1302,7 @@ void main() {
expect
(
hideNullProperty
.
value
,
isNull
);
expect
(
hideNullProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
hideNullProperty
.
toString
(),
equals
(
'name: missing'
));
validatePropertyJsonSerialization
(
hideNullProperty
);
final
DiagnosticsNode
hideName
=
new
DiagnosticsProperty
<
Rect
>.
lazy
(
'name'
,
...
...
@@ -1150,6 +1313,7 @@ void main() {
expect
(
hideName
.
value
,
equals
(
rect
));
expect
(
hideName
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
hideName
.
toString
(),
equals
(
'small rect'
));
validatePropertyJsonSerialization
(
hideName
);
final
DiagnosticsProperty
<
Object
>
throwingWithDescription
=
new
DiagnosticsProperty
<
Object
>.
lazy
(
'name'
,
...
...
@@ -1161,6 +1325,7 @@ void main() {
expect
(
throwingWithDescription
.
exception
,
isFlutterError
);
expect
(
throwingWithDescription
.
isFiltered
(
DiagnosticLevel
.
info
),
false
);
expect
(
throwingWithDescription
.
toString
(),
equals
(
'name: missing'
));
validatePropertyJsonSerialization
(
throwingWithDescription
);
final
DiagnosticsProperty
<
Object
>
throwingProperty
=
new
DiagnosticsProperty
<
Object
>.
lazy
(
'name'
,
...
...
@@ -1171,7 +1336,7 @@ void main() {
expect
(
throwingProperty
.
exception
,
isFlutterError
);
expect
(
throwingProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
false
);
expect
(
throwingProperty
.
toString
(),
equals
(
'name: EXCEPTION (FlutterError)'
));
validatePropertyJsonSerialization
(
throwingProperty
);
});
test
(
'color property test'
,
()
{
...
...
@@ -1182,9 +1347,12 @@ void main() {
'name'
,
color
,
);
validatePropertyJsonSerialization
(
simple
);
expect
(
simple
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
simple
.
value
,
equals
(
color
));
expect
(
simple
.
propertyType
,
equals
(
Color
));
expect
(
simple
.
toString
(),
equals
(
'name: Color(0xffffffff)'
));
validatePropertyJsonSerialization
(
simple
);
});
test
(
'flag property test'
,
()
{
...
...
@@ -1197,6 +1365,7 @@ void main() {
expect
(
show
.
value
,
isTrue
);
expect
(
show
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
show
.
toString
(),
equals
(
'layout computed'
));
validateFlagPropertyJsonSerialization
(
show
);
final
FlagProperty
hide
=
new
FlagProperty
(
'wasLayout'
,
...
...
@@ -1207,6 +1376,7 @@ void main() {
expect
(
hide
.
value
,
isFalse
);
expect
(
hide
.
level
,
equals
(
DiagnosticLevel
.
hidden
));
expect
(
hide
.
toString
(),
equals
(
'wasLayout: false'
));
validateFlagPropertyJsonSerialization
(
hide
);
final
FlagProperty
hideTrue
=
new
FlagProperty
(
'wasLayout'
,
...
...
@@ -1217,6 +1387,7 @@ void main() {
expect
(
hideTrue
.
value
,
isTrue
);
expect
(
hideTrue
.
level
,
equals
(
DiagnosticLevel
.
hidden
));
expect
(
hideTrue
.
toString
(),
equals
(
'wasLayout: true'
));
validateFlagPropertyJsonSerialization
(
hideTrue
);
});
test
(
'has property test'
,
()
{
...
...
@@ -1229,6 +1400,7 @@ void main() {
expect
(
has
.
value
,
equals
(
onClick
));
expect
(
has
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
has
.
toString
(),
equals
(
'has onClick'
));
validateObjectFlagPropertyJsonSerialization
(
has
);
final
ObjectFlagProperty
<
Function
>
missing
=
new
ObjectFlagProperty
<
Function
>.
has
(
'onClick'
,
...
...
@@ -1238,6 +1410,7 @@ void main() {
expect
(
missing
.
value
,
isNull
);
expect
(
missing
.
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
missing
.
toString
(),
equals
(
'onClick: null'
));
validateObjectFlagPropertyJsonSerialization
(
missing
);
});
test
(
'iterable property test'
,
()
{
...
...
@@ -1257,6 +1430,7 @@ void main() {
expect
(
emptyProperty
.
value
,
isEmpty
);
expect
(
emptyProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
emptyProperty
.
toString
(),
equals
(
'name: []'
));
validateIterablePropertyJsonSerialization
(
emptyProperty
);
final
IterableProperty
<
Object
>
nullProperty
=
new
IterableProperty
<
Object
>(
'list'
,
...
...
@@ -1265,6 +1439,7 @@ void main() {
expect
(
nullProperty
.
value
,
isNull
);
expect
(
nullProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isFalse
);
expect
(
nullProperty
.
toString
(),
equals
(
'list: null'
));
validateIterablePropertyJsonSerialization
(
nullProperty
);
final
IterableProperty
<
Object
>
hideNullProperty
=
new
IterableProperty
<
Object
>(
'list'
,
...
...
@@ -1275,6 +1450,7 @@ void main() {
expect
(
hideNullProperty
.
isFiltered
(
DiagnosticLevel
.
info
),
isTrue
);
expect
(
hideNullProperty
.
level
,
equals
(
DiagnosticLevel
.
fine
));
expect
(
hideNullProperty
.
toString
(),
equals
(
'list: null'
));
validateIterablePropertyJsonSerialization
(
hideNullProperty
);
final
List
<
Object
>
objects
=
<
Object
>[
new
Rect
.
fromLTRB
(
0.0
,
0.0
,
20.0
,
20.0
),
...
...
@@ -1290,6 +1466,7 @@ void main() {
objectsProperty
.
toString
(),
equals
(
'objects: Rect.fromLTRB(0.0, 0.0, 20.0, 20.0), Color(0xffffffff)'
),
);
validateIterablePropertyJsonSerialization
(
objectsProperty
);
final
IterableProperty
<
Object
>
multiLineProperty
=
new
IterableProperty
<
Object
>(
'objects'
,
...
...
@@ -1314,6 +1491,7 @@ void main() {
' Color(0xffffffff)
\n
'
,
),
);
validateIterablePropertyJsonSerialization
(
multiLineProperty
);
expect
(
new
TestTree
(
...
...
@@ -1356,6 +1534,7 @@ void main() {
objectProperty
.
toStringDeep
(),
equals
(
'object: Color(0xffffffff)
\n
'
),
);
validateIterablePropertyJsonSerialization
(
objectProperty
);
expect
(
new
TestTree
(
name:
'root'
,
...
...
@@ -1374,12 +1553,13 @@ void main() {
expect
(
message
.
name
,
isEmpty
);
expect
(
message
.
value
,
isNull
);
expect
(
message
.
showName
,
isFalse
);
validateNodeJsonSerialization
(
message
);
final
DiagnosticsNode
messageProperty
=
new
MessageProperty
(
'diagnostics'
,
'hello world'
);
expect
(
messageProperty
.
toString
(),
equals
(
'diagnostics: hello world'
));
expect
(
messageProperty
.
name
,
equals
(
'diagnostics'
));
expect
(
messageProperty
.
value
,
isNull
);
expect
(
messageProperty
.
showName
,
isTrue
);
validatePropertyJsonSerialization
(
messageProperty
);
});
}
packages/flutter/test/widgets/widget_inspector_test.dart
View file @
277001d1
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:convert'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -143,7 +145,6 @@ void main() {
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
childKey
)).
dy
,
equals
(
0.0
));
await
tester
.
fling
(
find
.
byType
(
ListView
),
const
Offset
(
0.0
,
-
200.0
),
200.0
);
...
...
@@ -256,4 +257,245 @@ void main() {
// objects to select as only 2 are onstage.
expect
(
inspectorState
.
selection
.
candidates
.
where
((
RenderObject
object
)
=>
object
is
RenderParagraph
).
length
,
equals
(
2
));
});
test
(
'WidgetInspectorService null id'
,
()
{
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
expect
(
service
.
toObject
(
null
),
isNull
);
expect
(
service
.
toId
(
null
,
'test-group'
),
isNull
);
});
test
(
'WidgetInspectorService dispose group'
,
()
{
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
Object
a
=
new
Object
();
final
String
group1
=
'group-1'
;
final
String
group2
=
'group-2'
;
final
String
group3
=
'group-3'
;
final
String
aId
=
service
.
toId
(
a
,
group1
);
expect
(
service
.
toId
(
a
,
group2
),
equals
(
aId
));
expect
(
service
.
toId
(
a
,
group3
),
equals
(
aId
));
service
.
disposeGroup
(
group1
);
service
.
disposeGroup
(
group2
);
expect
(
service
.
toObject
(
aId
),
equals
(
a
));
service
.
disposeGroup
(
group3
);
expect
(()
=>
service
.
toObject
(
aId
),
throwsFlutterError
);
});
test
(
'WidgetInspectorService dispose id'
,
()
{
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
Object
a
=
new
Object
();
final
Object
b
=
new
Object
();
final
String
group1
=
'group-1'
;
final
String
group2
=
'group-2'
;
final
String
aId
=
service
.
toId
(
a
,
group1
);
final
String
bId
=
service
.
toId
(
b
,
group1
);
expect
(
service
.
toId
(
a
,
group2
),
equals
(
aId
));
service
.
disposeId
(
bId
,
group1
);
expect
(()
=>
service
.
toObject
(
bId
),
throwsFlutterError
);
service
.
disposeId
(
aId
,
group1
);
expect
(
service
.
toObject
(
aId
),
equals
(
a
));
service
.
disposeId
(
aId
,
group2
);
expect
(()
=>
service
.
toObject
(
aId
),
throwsFlutterError
);
});
test
(
'WidgetInspectorService toObjectForSourceLocation'
,
()
{
final
String
group
=
'test-group'
;
final
Text
widget
=
const
Text
(
'a'
,
textDirection:
TextDirection
.
ltr
);
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
String
id
=
service
.
toId
(
widget
,
group
);
expect
(
service
.
toObjectForSourceLocation
(
id
),
equals
(
widget
));
final
Element
element
=
widget
.
createElement
();
final
String
elementId
=
service
.
toId
(
element
,
group
);
expect
(
service
.
toObjectForSourceLocation
(
elementId
),
equals
(
widget
));
expect
(
element
,
isNot
(
equals
(
widget
)));
service
.
disposeGroup
(
group
);
expect
(()
=>
service
.
toObjectForSourceLocation
(
elementId
),
throwsFlutterError
);
});
test
(
'WidgetInspectorService object id test'
,
()
{
final
Text
a
=
const
Text
(
'a'
,
textDirection:
TextDirection
.
ltr
);
final
Text
b
=
const
Text
(
'b'
,
textDirection:
TextDirection
.
ltr
);
final
Text
c
=
const
Text
(
'c'
,
textDirection:
TextDirection
.
ltr
);
final
Text
d
=
const
Text
(
'd'
,
textDirection:
TextDirection
.
ltr
);
final
String
group1
=
'group-1'
;
final
String
group2
=
'group-2'
;
final
String
group3
=
'group-3'
;
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
String
aId
=
service
.
toId
(
a
,
group1
);
final
String
bId
=
service
.
toId
(
b
,
group2
);
final
String
cId
=
service
.
toId
(
c
,
group3
);
final
String
dId
=
service
.
toId
(
d
,
group1
);
// Make sure we get a consistent id if we add the object to a group multiple
// times.
expect
(
aId
,
equals
(
service
.
toId
(
a
,
group1
)));
expect
(
service
.
toObject
(
aId
),
equals
(
a
));
expect
(
service
.
toObject
(
aId
),
isNot
(
equals
(
b
)));
expect
(
service
.
toObject
(
bId
),
equals
(
b
));
expect
(
service
.
toObject
(
cId
),
equals
(
c
));
expect
(
service
.
toObject
(
dId
),
equals
(
d
));
// Make sure we get a consistent id even if we add the object to a different
// group.
expect
(
aId
,
equals
(
service
.
toId
(
a
,
group3
)));
expect
(
aId
,
isNot
(
equals
(
bId
)));
expect
(
aId
,
isNot
(
equals
(
cId
)));
service
.
disposeGroup
(
group3
);
});
testWidgets
(
'WidgetInspectorService maybeSetSelection'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Stack
(
children:
<
Widget
>[
const
Text
(
'a'
,
textDirection:
TextDirection
.
ltr
),
const
Text
(
'b'
,
textDirection:
TextDirection
.
ltr
),
const
Text
(
'c'
,
textDirection:
TextDirection
.
ltr
),
],
),
),
);
final
Element
elementA
=
find
.
text
(
'a'
).
evaluate
().
first
;
final
Element
elementB
=
find
.
text
(
'b'
).
evaluate
().
first
;
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
service
.
selection
.
clear
();
int
selectionChangedCount
=
0
;
service
.
selectionChangedCallback
=
()
=>
selectionChangedCount
++;
service
.
setSelection
(
'invalid selection'
);
expect
(
selectionChangedCount
,
equals
(
0
));
expect
(
service
.
selection
.
currentElement
,
isNull
);
service
.
setSelection
(
elementA
);
expect
(
selectionChangedCount
,
equals
(
1
));
expect
(
service
.
selection
.
currentElement
,
equals
(
elementA
));
expect
(
service
.
selection
.
current
,
equals
(
elementA
.
renderObject
));
service
.
setSelection
(
elementB
.
renderObject
);
expect
(
selectionChangedCount
,
equals
(
2
));
expect
(
service
.
selection
.
current
,
equals
(
elementB
.
renderObject
));
expect
(
service
.
selection
.
currentElement
,
equals
(
elementB
.
renderObject
.
debugCreator
.
element
));
service
.
setSelection
(
'invalid selection'
);
expect
(
selectionChangedCount
,
equals
(
2
));
expect
(
service
.
selection
.
current
,
equals
(
elementB
.
renderObject
));
service
.
setSelectionById
(
service
.
toId
(
elementA
,
'my-group'
));
expect
(
selectionChangedCount
,
equals
(
3
));
expect
(
service
.
selection
.
currentElement
,
equals
(
elementA
));
expect
(
service
.
selection
.
current
,
equals
(
elementA
.
renderObject
));
service
.
setSelectionById
(
service
.
toId
(
elementA
,
'my-group'
));
expect
(
selectionChangedCount
,
equals
(
3
));
expect
(
service
.
selection
.
currentElement
,
equals
(
elementA
));
});
testWidgets
(
'WidgetInspectorService getParentChain'
,
(
WidgetTester
tester
)
async
{
final
String
group
=
'test-group'
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Stack
(
children:
<
Widget
>[
const
Text
(
'a'
,
textDirection:
TextDirection
.
ltr
),
const
Text
(
'b'
,
textDirection:
TextDirection
.
ltr
),
const
Text
(
'c'
,
textDirection:
TextDirection
.
ltr
),
],
),
),
);
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
Element
elementB
=
find
.
text
(
'b'
).
evaluate
().
first
;
final
String
bId
=
service
.
toId
(
elementB
,
group
);
final
Object
json
=
JSON
.
decode
(
service
.
getParentChain
(
bId
,
group
));
expect
(
json
,
isList
);
final
List
<
Object
>
chainElements
=
json
;
final
List
<
Element
>
expectedChain
=
elementB
.
debugGetDiagnosticChain
()?.
reversed
?.
toList
();
// Sanity check that the chain goes back to the root.
expect
(
expectedChain
.
first
,
tester
.
binding
.
renderViewElement
);
expect
(
chainElements
.
length
,
equals
(
expectedChain
.
length
));
for
(
int
i
=
0
;
i
<
expectedChain
.
length
;
i
+=
1
)
{
expect
(
chainElements
[
i
],
isMap
);
final
Map
<
String
,
Object
>
chainNode
=
chainElements
[
i
];
final
Element
element
=
expectedChain
[
i
];
expect
(
chainNode
[
'node'
],
isMap
);
final
Map
<
String
,
Object
>
jsonNode
=
chainNode
[
'node'
];
expect
(
service
.
toObject
(
jsonNode
[
'valueId'
]),
equals
(
element
));
expect
(
service
.
toObject
(
jsonNode
[
'objectId'
]),
const
isInstanceOf
<
DiagnosticsNode
>());
expect
(
chainNode
[
'children'
],
isList
);
final
List
<
Object
>
jsonChildren
=
chainNode
[
'children'
];
final
List
<
Element
>
childrenElements
=
<
Element
>[];
element
.
visitChildren
(
childrenElements
.
add
);
expect
(
jsonChildren
.
length
,
equals
(
childrenElements
.
length
));
if
(
i
+
1
==
expectedChain
.
length
)
{
expect
(
chainNode
[
'childIndex'
],
isNull
);
}
else
{
expect
(
chainNode
[
'childIndex'
],
equals
(
childrenElements
.
indexOf
(
expectedChain
[
i
+
1
])));
}
for
(
int
j
=
0
;
j
<
childrenElements
.
length
;
j
+=
1
)
{
expect
(
jsonChildren
[
j
],
isMap
);
final
Map
<
String
,
Object
>
childJson
=
jsonChildren
[
j
];
expect
(
service
.
toObject
(
childJson
[
'valueId'
]),
equals
(
childrenElements
[
j
]));
expect
(
service
.
toObject
(
childJson
[
'objectId'
]),
const
isInstanceOf
<
DiagnosticsNode
>());
}
}
});
test
(
'WidgetInspectorService getProperties'
,
()
{
final
DiagnosticsNode
diagnostic
=
const
Text
(
'a'
,
textDirection:
TextDirection
.
ltr
).
toDiagnosticsNode
();
final
String
group
=
'group'
;
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
String
id
=
service
.
toId
(
diagnostic
,
group
);
final
List
<
Object
>
propertiesJson
=
JSON
.
decode
(
service
.
getProperties
(
id
,
group
));
final
List
<
DiagnosticsNode
>
properties
=
diagnostic
.
getProperties
();
expect
(
properties
,
isNotEmpty
);
expect
(
propertiesJson
.
length
,
equals
(
properties
.
length
));
for
(
int
i
=
0
;
i
<
propertiesJson
.
length
;
++
i
)
{
final
Map
<
String
,
Object
>
propertyJson
=
propertiesJson
[
i
];
expect
(
service
.
toObject
(
propertyJson
[
'valueId'
]),
equals
(
properties
[
i
].
value
));
expect
(
service
.
toObject
(
propertyJson
[
'objectId'
]),
const
isInstanceOf
<
DiagnosticsNode
>());
}
});
testWidgets
(
'WidgetInspectorService getChildren'
,
(
WidgetTester
tester
)
async
{
final
String
group
=
'test-group'
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Stack
(
children:
<
Widget
>[
const
Text
(
'a'
,
textDirection:
TextDirection
.
ltr
),
const
Text
(
'b'
,
textDirection:
TextDirection
.
ltr
),
const
Text
(
'c'
,
textDirection:
TextDirection
.
ltr
),
],
),
),
);
final
DiagnosticsNode
diagnostic
=
find
.
byType
(
Stack
).
evaluate
().
first
.
toDiagnosticsNode
();
final
WidgetInspectorService
service
=
WidgetInspectorService
.
instance
;
service
.
disposeAllGroups
();
final
String
id
=
service
.
toId
(
diagnostic
,
group
);
final
List
<
Object
>
propertiesJson
=
JSON
.
decode
(
service
.
getChildren
(
id
,
group
));
final
List
<
DiagnosticsNode
>
children
=
diagnostic
.
getChildren
();
expect
(
children
.
length
,
equals
(
3
));
expect
(
propertiesJson
.
length
,
equals
(
children
.
length
));
for
(
int
i
=
0
;
i
<
propertiesJson
.
length
;
++
i
)
{
final
Map
<
String
,
Object
>
propertyJson
=
propertiesJson
[
i
];
expect
(
service
.
toObject
(
propertyJson
[
'valueId'
]),
equals
(
children
[
i
].
value
));
expect
(
service
.
toObject
(
propertyJson
[
'objectId'
]),
const
isInstanceOf
<
DiagnosticsNode
>());
}
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment