Field Display initial implementation (#4322)
Field Display initial implementation for the object inspector tool
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics_view.dart
index 308de4b..963ce01 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/isolate_statistics_view.dart
@@ -78,12 +78,12 @@
Flexible(
child: Column(
children: [
- Flexible(
+ Expanded(
child: GeneralIsolateStatisticsWidget(
controller: controller,
),
),
- Flexible(
+ Expanded(
child: IsolateMemoryStatisticsWidget(
controller: controller,
),
@@ -177,34 +177,28 @@
@override
Widget build(BuildContext context) {
final isolate = controller.isolate;
- return Column(
- children: [
- Flexible(
- child: VMInfoCard(
- title: 'Memory',
- rowKeyValues: [
- selectableTextBuilderMapEntry(
- 'Dart Heap',
- _buildMemoryString(
- isolate?.dartHeapSize,
- isolate?.dartHeapCapacity,
- ),
- ),
- selectableTextBuilderMapEntry(
- 'New Space',
- _buildMemoryString(
- isolate?.newSpaceUsage,
- isolate?.newSpaceUsage,
- ),
- ),
- selectableTextBuilderMapEntry(
- 'Old Space',
- _buildMemoryString(
- isolate?.oldSpaceUsage,
- isolate?.oldSpaceCapacity,
- ),
- ),
- ],
+ return VMInfoCard(
+ title: 'Memory',
+ rowKeyValues: [
+ selectableTextBuilderMapEntry(
+ 'Dart Heap',
+ _buildMemoryString(
+ isolate?.dartHeapSize,
+ isolate?.dartHeapCapacity,
+ ),
+ ),
+ selectableTextBuilderMapEntry(
+ 'New Space',
+ _buildMemoryString(
+ isolate?.newSpaceUsage,
+ isolate?.newSpaceUsage,
+ ),
+ ),
+ selectableTextBuilderMapEntry(
+ 'Old Space',
+ _buildMemoryString(
+ isolate?.oldSpaceUsage,
+ isolate?.oldSpaceCapacity,
),
),
],
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/object_viewport.dart b/packages/devtools_app/lib/src/screens/vm_developer/object_viewport.dart
index f8f2894..e14172a 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/object_viewport.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/object_viewport.dart
@@ -12,6 +12,7 @@
import 'vm_class_display.dart';
import 'vm_code_display.dart';
import 'vm_developer_common_widgets.dart';
+import 'vm_field_display.dart';
import 'vm_object_model.dart';
/// Displays the VM information for the currently selected object in the
@@ -81,7 +82,7 @@
return const VMInfoCard(title: 'TO-DO: Display Function object data');
}
if (obj is FieldObject) {
- return const VMInfoCard(title: 'TO-DO: Display Field object data');
+ return VmFieldDisplay(field: obj);
}
if (obj is LibraryObject) {
return const VMInfoCard(title: 'TO-DO: Display Library object data');
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_class_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_class_display.dart
index e41ff30..9b865ba 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_class_display.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_class_display.dart
@@ -5,10 +5,6 @@
import 'package:flutter/material.dart';
import 'package:vm_service/vm_service.dart';
-import '../../primitives/utils.dart';
-import '../../shared/common_widgets.dart';
-import '../../shared/table.dart';
-import '../../shared/theme.dart';
import 'vm_developer_common_widgets.dart';
import 'vm_object_model.dart';
@@ -30,27 +26,10 @@
Widget build(BuildContext context) {
return Row(
children: [
- Expanded(
- child: Column(
- children: [
- ClassInfoWidget(
- classDataRows: _classDataRows(clazz),
- ),
- Flexible(
- child: ListView(
- children: [
- RetainingPathWidget(
- retainingPath: clazz.retainingPath,
- onExpanded: _onExpandRetainingPath,
- ),
- InboundReferencesWidget(
- inboundReferences: clazz.inboundReferences,
- onExpanded: _onExpandInboundRefs,
- ),
- ],
- ),
- ),
- ],
+ Flexible(
+ child: VmObjectDisplayBasicLayout(
+ object: clazz,
+ generalDataRows: _classDataRows(clazz),
),
),
if (displayClassInstances)
@@ -62,91 +41,17 @@
],
);
}
-
- void _onExpandRetainingPath(bool expanded) {
- if (clazz.retainingPath.value == null) clazz.requestRetainingPath();
- }
-
- void _onExpandInboundRefs(bool expanded) {
- if (clazz.inboundReferences.value == null) clazz.requestInboundsRefs();
- }
}
-/// Displays general VM information of the Class Object.
-class ClassInfoWidget extends StatelessWidget implements PreferredSizeWidget {
- const ClassInfoWidget({
- required this.classDataRows,
- });
-
- final List<MapEntry<String, Widget Function(BuildContext)>> classDataRows;
-
- @override
- Widget build(BuildContext context) {
- return SizedBox.fromSize(
- size: preferredSize,
- child: VMInfoCard(
- title: 'General Information',
- rowKeyValues: classDataRows,
- ),
- );
- }
-
- @override
- Size get preferredSize => Size.fromHeight(
- areaPaneHeaderHeight +
- classDataRows.length * defaultRowHeight +
- defaultSpacing,
- );
-}
-
-List<MapEntry<String, Widget Function(BuildContext)>> _classDataRows(
+// TODO(mtaylee): Delete 'Currently allocated instances' row when
+// ClassInstancesWidget implementation is completed.
+/// Generates a list of key-value pairs (map entries) containing the general
+/// information of the class object [clazz].
+List<MapEntry<String, WidgetBuilder>> _classDataRows(
ClassObject clazz,
) {
return [
- selectableTextBuilderMapEntry('Object Class', clazz.obj.type),
- selectableTextBuilderMapEntry(
- 'Shallow Size',
- prettyPrintBytes(
- clazz.obj.size ?? 0,
- includeUnit: true,
- kbFractionDigits: 1,
- maxBytes: 512,
- ),
- ),
- MapEntry(
- 'Reachable Size',
- (context) => ValueListenableBuilder<bool>(
- valueListenable: clazz.fetchingReachableSize,
- builder: (context, fetching, _) => fetching
- ? const CircularProgressIndicator()
- : RequestableSizeWidget(
- requestedSize: clazz.reachableSize,
- requestFunction: clazz.requestReachableSize,
- ),
- ),
- ),
- MapEntry(
- 'Retained Size',
- (context) => ValueListenableBuilder<bool>(
- valueListenable: clazz.fetchingRetainedSize,
- builder: (context, fetching, _) => fetching
- ? const CircularProgressIndicator()
- : RequestableSizeWidget(
- requestedSize: clazz.retainedSize,
- requestFunction: clazz.requestRetainedSize,
- ),
- ),
- ),
- selectableTextBuilderMapEntry(
- 'Library',
- clazz.obj.library?.name?.isEmpty ?? false
- ? clazz.script?.uri
- : clazz.obj.library?.name,
- ),
- selectableTextBuilderMapEntry(
- 'Script',
- '${fileNameFromUri(clazz.script?.uri) ?? ''}:${clazz.pos?.toString() ?? ''}',
- ),
+ ...vmObjectGeneralDataRows(clazz),
selectableTextBuilderMapEntry('Superclass', clazz.obj.superClass?.name),
selectableTextBuilderMapEntry('SuperType', clazz.obj.superType?.name),
selectableTextBuilderMapEntry(
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
index 5ac49d4..97ebd76 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_developer_common_widgets.dart
@@ -10,6 +10,7 @@
import '../../shared/common_widgets.dart';
import '../../shared/table.dart';
import '../../shared/theme.dart';
+import 'vm_object_model.dart';
import 'vm_service_private_extensions.dart';
/// A convenience widget used to create non-scrollable information cards.
@@ -29,16 +30,19 @@
});
final String title;
- final List<MapEntry<String, Widget Function(BuildContext)>>? rowKeyValues;
+ final List<MapEntry<String, WidgetBuilder>>? rowKeyValues;
final Widget? table;
@override
Widget build(BuildContext context) {
- return Card(
- child: VMInfoList(
- title: title,
- rowKeyValues: rowKeyValues,
- table: table,
+ return SizedBox.fromSize(
+ size: preferredSize,
+ child: Card(
+ child: VMInfoList(
+ title: title,
+ rowKeyValues: rowKeyValues,
+ table: table,
+ ),
),
);
}
@@ -56,7 +60,7 @@
}
}
-MapEntry<String, Widget Function(BuildContext)> selectableTextBuilderMapEntry(
+MapEntry<String, WidgetBuilder> selectableTextBuilderMapEntry(
String key,
String? value,
) {
@@ -77,7 +81,7 @@
});
final String title;
- final List<MapEntry<String, Widget Function(BuildContext)>>? rowKeyValues;
+ final List<MapEntry<String, WidgetBuilder>>? rowKeyValues;
final Widget? table;
@override
@@ -204,7 +208,7 @@
objectRefName =
(objectRef.name?.isEmpty ?? false) ? objectRef.uri : objectRef.name;
} else if (objectRef is ScriptRef) {
- objectRefName = fileNameFromUri(objectRef.uri);
+ objectRefName = _fileNameFromUri(objectRef.uri);
} else if (objectRef is InstanceRef) {
objectRefName = objectRef.name ??
'Instance of ${objectRef.classRef?.name ?? '<Class>'}';
@@ -215,6 +219,31 @@
return objectRefName;
}
+/// Returns the owner name of a Field or Func object, if [object] is a
+/// FuncObject, it returns the complete (qualified) name of the owner.
+String? _ownerName(VmObject object) {
+ assert(object is FieldObject || object is FuncObject);
+ final owner = (object as dynamic).obj.owner as ObjRef?;
+
+ if (owner == null) {
+ return object.script?.uri;
+ }
+
+ if (object is FieldObject) {
+ if (owner is ClassRef || owner is LibraryRef) {
+ return _objectName(owner);
+ }
+ } else if (object is FuncObject) {
+ if (owner is LibraryRef) {
+ return _objectName(owner);
+ } else {
+ return qualifiedName(owner);
+ }
+ }
+
+ throw Exception('Unexpected owner type: ${owner.type}');
+}
+
/// Returns the name of a function, qualified with the name of
/// its owner added as a prefix, separated by a period.
///
@@ -518,8 +547,139 @@
}
}
-String? fileNameFromUri(String? uri) {
+String? _fileNameFromUri(String? uri) {
if (uri == null) return null;
final splitted = uri.split('/');
return splitted[splitted.length - 1];
}
+
+/// A widget for the object inspector historyViewport containing the main
+/// layout of information widgets related to VM object types.
+class VmObjectDisplayBasicLayout extends StatelessWidget {
+ const VmObjectDisplayBasicLayout({
+ required this.object,
+ required this.generalDataRows,
+ this.sideCardDataRows,
+ this.generalInfoTitle = 'General Information',
+ this.sideCardTitle = 'Object Details',
+ this.expandableWidgets,
+ });
+
+ final VmObject object;
+ final List<MapEntry<String, WidgetBuilder>> generalDataRows;
+ final List<MapEntry<String, WidgetBuilder>>? sideCardDataRows;
+ final String generalInfoTitle;
+ final String sideCardTitle;
+ final List<Widget>? expandableWidgets;
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ IntrinsicHeight(
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Flexible(
+ child: VMInfoCard(
+ title: generalInfoTitle,
+ rowKeyValues: generalDataRows,
+ ),
+ ),
+ if (sideCardDataRows != null)
+ Flexible(
+ child: VMInfoCard(
+ title: sideCardTitle,
+ rowKeyValues: sideCardDataRows,
+ ),
+ ),
+ ],
+ ),
+ ),
+ Flexible(
+ child: ListView(
+ children: [
+ RetainingPathWidget(
+ retainingPath: object.retainingPath,
+ onExpanded: _onExpandRetainingPath,
+ ),
+ InboundReferencesWidget(
+ inboundReferences: object.inboundReferences,
+ onExpanded: _onExpandInboundRefs,
+ ),
+ ...?expandableWidgets,
+ ],
+ ),
+ ),
+ ],
+ );
+ }
+
+ void _onExpandRetainingPath(bool expanded) {
+ if (object.retainingPath.value == null) {
+ object.requestRetainingPath();
+ }
+ }
+
+ void _onExpandInboundRefs(bool expanded) {
+ if (object.inboundReferences.value == null) {
+ object.requestInboundsRefs();
+ }
+ }
+}
+
+List<MapEntry<String, WidgetBuilder>> vmObjectGeneralDataRows(
+ VmObject object,
+) {
+ return [
+ selectableTextBuilderMapEntry('Object Class', object.obj.type),
+ selectableTextBuilderMapEntry(
+ 'Shallow Size',
+ prettyPrintBytes(
+ object.obj.size ?? 0,
+ includeUnit: true,
+ kbFractionDigits: 1,
+ maxBytes: 512,
+ ),
+ ),
+ MapEntry(
+ 'Reachable Size',
+ (context) => ValueListenableBuilder<bool>(
+ valueListenable: object.fetchingReachableSize,
+ builder: (context, fetching, _) => fetching
+ ? const CircularProgressIndicator()
+ : RequestableSizeWidget(
+ requestedSize: object.reachableSize,
+ requestFunction: object.requestReachableSize,
+ ),
+ ),
+ ),
+ MapEntry(
+ 'Retained Size',
+ (context) => ValueListenableBuilder<bool>(
+ valueListenable: object.fetchingRetainedSize,
+ builder: (context, fetching, _) => fetching
+ ? const CircularProgressIndicator()
+ : RequestableSizeWidget(
+ requestedSize: object.retainedSize,
+ requestFunction: object.requestRetainedSize,
+ ),
+ ),
+ ),
+ if (object is ClassObject)
+ selectableTextBuilderMapEntry(
+ 'Library',
+ _objectName(object.obj.library),
+ ),
+ if (object is FieldObject || object is FuncObject)
+ selectableTextBuilderMapEntry(
+ 'Owner',
+ _ownerName(object),
+ ),
+ if (object is! ScriptObject && object is! LibraryObject)
+ selectableTextBuilderMapEntry(
+ 'Script',
+ '${_fileNameFromUri(object.script?.uri) ?? ''}:${object.pos?.toString() ?? ''}',
+ ),
+ ];
+}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_field_display.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_field_display.dart
new file mode 100644
index 0000000..f161fe5
--- /dev/null
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_field_display.dart
@@ -0,0 +1,77 @@
+// Copyright 2022 The Chromium 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/material.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'vm_developer_common_widgets.dart';
+import 'vm_object_model.dart';
+import 'vm_service_private_extensions.dart';
+
+/// A widget for the object inspector historyViewport, displaying information
+/// related to field objects in the Dart VM.
+class VmFieldDisplay extends StatelessWidget {
+ const VmFieldDisplay({
+ required this.field,
+ });
+
+ final FieldObject field;
+
+ @override
+ Widget build(BuildContext context) => VmObjectDisplayBasicLayout(
+ object: field,
+ generalDataRows: _fieldDataRows(field),
+ );
+}
+
+/// Generates a list of key-value pairs (map entries) containing the general
+/// information of the field object [field].
+List<MapEntry<String, WidgetBuilder>> _fieldDataRows(
+ FieldObject field,
+) {
+ return [
+ ...vmObjectGeneralDataRows(field),
+ selectableTextBuilderMapEntry(
+ 'Observed types',
+ _fieldObservedTypes(field),
+ ),
+ if (field.obj.staticValue is InstanceRef)
+ selectableTextBuilderMapEntry(
+ 'Static Value',
+ '${field.obj.staticValue.name ?? field.obj.staticValue.classRef.name}: '
+ '${field.obj.staticValue.valueAsString ?? 'Unknown value'}',
+ ),
+ ];
+}
+
+/// Returns the observed types of a field object, including null.
+///
+/// The observed types can be a single type (guardClassSingle), various types
+/// (guardClassDynamic), or a type that has not been observed yet
+/// (guardClassUnknown).
+String _fieldObservedTypes(FieldObject field) {
+ String type;
+
+ final kind = field.guardClassKind;
+
+ switch (kind) {
+ case GuardClassKind.single:
+ type = field.guardClass!.name ?? '<Observed Type>';
+ break;
+ case GuardClassKind.dynamic:
+ type = GuardClassKind.dynamic.jsonValue();
+ break;
+ case GuardClassKind.unknown:
+ type = 'none';
+ break;
+ default:
+ type = 'Observed types not found';
+ }
+
+ final nullable = field.guardNullable == null
+ ? ''
+ : ' - null ${field.guardNullable! ? '' : 'not '}observed';
+
+ return '$type$nullable';
+}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_object_model.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_object_model.dart
index 12568e3..9b4beba 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_object_model.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_object_model.dart
@@ -9,6 +9,7 @@
import '../../shared/globals.dart';
import '../debugger/debugger_model.dart';
import '../debugger/program_explorer_model.dart';
+import 'vm_service_private_extensions.dart';
/// Wrapper class for storing Dart VM objects with their relevant VM
/// information.
@@ -109,7 +110,8 @@
}
}
-//TODO(mtaylee): finish class implementation.
+/// Stores a 'Class' VM object and provides an interface for obtaining the
+/// Dart VM information related to this object.
class ClassObject extends VmObject {
ClassObject({required super.ref, super.scriptRef, super.outlineNode});
@@ -146,7 +148,8 @@
SourceLocation? get _sourceLocation => obj.location;
}
-//TODO(mtaylee): finish class implementation.
+/// Stores a 'Field' VM object and provides an interface for obtaining the
+/// Dart VM information related to this object.
class FieldObject extends VmObject {
FieldObject({required super.ref, super.scriptRef, super.outlineNode});
@@ -158,6 +161,26 @@
@override
SourceLocation? get _sourceLocation => obj.location;
+
+ late final bool? guardNullable;
+
+ late final Class? guardClass;
+
+ late final GuardClassKind? guardClassKind;
+
+ @override
+ Future<void> initialize() async {
+ await super.initialize();
+
+ guardNullable = obj.guardNullable;
+ guardClassKind = obj.guardClassKind();
+
+ if (guardClassKind == GuardClassKind.single) {
+ guardClass = await obj.guardClass;
+ } else {
+ guardClass = null;
+ }
+ }
}
//TODO(mtaylee): finish class implementation.
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
index d893d56..835101b 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_service_private_extensions.dart
@@ -4,6 +4,8 @@
import 'package:vm_service/vm_service.dart';
+import '../../shared/globals.dart';
+
/// NOTE: this file contains extensions to classes provided by
/// `package:vm_service` in order to expose VM internal fields in a controlled
/// fashion. Objects and extensions in this class should not be used in
@@ -172,7 +174,6 @@
}
/// An extension on [Func] which allows for access to VM internal fields.
-
extension FunctionPrivateViewExtension on Func {
static const _unoptimizedCodeKey = '_unoptimizedCode';
@@ -182,7 +183,6 @@
}
/// An extension on [Code] which allows for access to VM internal fields.
-
extension CodePrivateViewExtension on Code {
static const _disassemblyKey = '_disassembly';
@@ -192,3 +192,67 @@
set disassembly(Disassembly disassembly) =>
json![_disassemblyKey] = disassembly.toJson();
}
+
+/// An extension on [Field] which allows for access to VM internal fields.
+extension FieldPrivateViewExtension on Field {
+ static const guardClassKey = '_guardClass';
+
+ bool? get guardNullable => json!['_guardNullable'];
+
+ Future<Class?> get guardClass async {
+ if (_guardClassIsClass()) {
+ final service = serviceManager.service!;
+ final isolate = serviceManager.isolateManager.selectedIsolate.value;
+
+ return await service.getObject(isolate!.id!, json![guardClassKey]['id'])
+ as Class;
+ }
+
+ return null;
+ }
+
+ GuardClassKind? guardClassKind() {
+ if (_guardClassIsClass()) {
+ return GuardClassKind.single;
+ } else if (json![guardClassKey] == GuardClassKind.dynamic.jsonValue()) {
+ return GuardClassKind.dynamic;
+ } else if (json![guardClassKey] == GuardClassKind.unknown.jsonValue()) {
+ return GuardClassKind.unknown;
+ }
+
+ return null;
+ }
+
+ bool _guardClassIsClass() {
+ String? guardClassType;
+
+ if (json![guardClassKey] is Map) {
+ guardClassType = json![guardClassKey]['type'];
+ }
+
+ if (guardClassType == '@Class' || guardClassType == 'Class') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
+
+/// The kinds of Guard Class that determine whether a Field object has
+/// a unique observed type [single], various observed types [dynamic],
+/// or if the field type has not been observed yet [unknown].
+enum GuardClassKind {
+ single,
+ dynamic,
+ unknown;
+
+ String jsonValue() {
+ switch (this) {
+ case GuardClassKind.dynamic:
+ return 'various';
+ case GuardClassKind.single:
+ case GuardClassKind.unknown:
+ return toString().split('.').last;
+ }
+ }
+}
diff --git a/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics_view.dart b/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics_view.dart
index 11abc90..5f8655b 100644
--- a/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics_view.dart
+++ b/packages/devtools_app/lib/src/screens/vm_developer/vm_statistics_view.dart
@@ -70,12 +70,12 @@
flex: 3,
child: Column(
children: [
- Flexible(
+ Expanded(
child: GeneralVMStatisticsWidget(
controller: controller,
),
),
- Flexible(
+ Expanded(
child: ProcessStatisticsWidget(
controller: controller,
),
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/object_inspector_view_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/object_inspector_view_test.dart
index 22a65c4..1422771 100644
--- a/packages/devtools_app/test/vm_developer/object_inspector/object_inspector_view_test.dart
+++ b/packages/devtools_app/test/vm_developer/object_inspector/object_inspector_view_test.dart
@@ -2,10 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/config_specific/ide_theme/ide_theme.dart';
import 'package:devtools_app/src/screens/debugger/program_explorer.dart';
import 'package:devtools_app/src/screens/vm_developer/object_inspector_view.dart';
import 'package:devtools_app/src/screens/vm_developer/object_viewport.dart';
+import 'package:devtools_app/src/scripts/script_manager.dart';
+import 'package:devtools_app/src/service/service_manager.dart';
+import 'package:devtools_app/src/shared/globals.dart';
+import 'package:devtools_app/src/shared/split.dart';
import 'package:devtools_test/devtools_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/object_viewport_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/object_viewport_test.dart
index d6adace..732f151 100644
--- a/packages/devtools_app/test/vm_developer/object_inspector/object_viewport_test.dart
+++ b/packages/devtools_app/test/vm_developer/object_inspector/object_viewport_test.dart
@@ -2,10 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/config_specific/ide_theme/ide_theme.dart';
import 'package:devtools_app/src/screens/vm_developer/object_viewport.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_class_display.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_developer_common_widgets.dart';
+import 'package:devtools_app/src/screens/vm_developer/vm_field_display.dart';
+import 'package:devtools_app/src/screens/vm_developer/vm_object_model.dart';
+import 'package:devtools_app/src/scripts/script_manager.dart';
+import 'package:devtools_app/src/service/service_manager.dart';
+import 'package:devtools_app/src/shared/globals.dart';
import 'package:devtools_app/src/shared/history_viewport.dart';
import 'package:devtools_test/devtools_test.dart';
import 'package:flutter/material.dart';
@@ -19,6 +24,8 @@
late MockClassObject mockClassObject;
+ late MockFieldObject mockFieldObject;
+
late FakeServiceManager fakeServiceManager;
late MockScriptManager scriptManager;
@@ -53,12 +60,33 @@
when(mockClassObject.fetchingRetainedSize)
.thenReturn(ValueNotifier<bool>(false));
when(mockClassObject.retainedSize).thenReturn(null);
- when(mockClassObject.retainingPath).thenReturn(
- ValueNotifier<RetainingPath?>(null),
- );
- when(mockClassObject.inboundReferences).thenReturn(
- ValueNotifier<InboundReferences?>(null),
- );
+ when(mockClassObject.retainingPath)
+ .thenReturn(ValueNotifier<RetainingPath?>(null));
+ when(mockClassObject.inboundReferences)
+ .thenReturn(ValueNotifier<InboundReferences?>(null));
+
+ mockFieldObject = MockFieldObject();
+
+ when(mockFieldObject.outlineNode).thenReturn(null);
+ when(mockFieldObject.scriptRef).thenReturn(null);
+ when(mockFieldObject.name).thenReturn(testField.name);
+ when(mockFieldObject.ref).thenReturn(testField);
+ when(mockFieldObject.obj).thenReturn(testField);
+ when(mockFieldObject.script).thenReturn(null);
+ when(mockFieldObject.pos).thenReturn(null);
+ when(mockFieldObject.guardClass).thenReturn(null);
+ when(mockFieldObject.guardNullable).thenReturn(null);
+ when(mockFieldObject.guardClassKind).thenReturn(null);
+ when(mockFieldObject.fetchingReachableSize)
+ .thenReturn(ValueNotifier<bool>(false));
+ when(mockFieldObject.reachableSize).thenReturn(null);
+ when(mockFieldObject.fetchingRetainedSize)
+ .thenReturn(ValueNotifier<bool>(false));
+ when(mockFieldObject.retainedSize).thenReturn(null);
+ when(mockFieldObject.retainingPath)
+ .thenReturn(ValueNotifier<RetainingPath?>(null));
+ when(mockFieldObject.inboundReferences)
+ .thenReturn(ValueNotifier<InboundReferences?>(null));
});
testWidgets('builds object viewport', (WidgetTester tester) async {
@@ -99,16 +127,16 @@
});
testWidgets('test for Field Object', (WidgetTester tester) async {
- final testFieldObject =
- TestFieldObject(ref: testField, testField: testField);
testObjectInspectorViewController.fakeObjectHistory
- .setCurrentObject(testFieldObject);
+ .setCurrentObject(mockFieldObject);
+
await tester.pumpWidget(
wrap(ObjectViewport(controller: testObjectInspectorViewController)),
);
- expect(viewportTitle(testFieldObject), 'Field FooField');
- expect(find.text('Field FooField'), findsOneWidget);
- expect(find.byType(VMInfoCard), findsOneWidget);
+
+ expect(viewportTitle(mockFieldObject), 'Field fooField');
+ expect(find.text('Field fooField'), findsOneWidget);
+ expect(find.byType(VmFieldDisplay), findsOneWidget);
});
testWidgets('test for Library Object', (WidgetTester tester) async {
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/vm_class_display_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/vm_class_display_test.dart
index e369692..bc65f58 100644
--- a/packages/devtools_app/test/vm_developer/object_inspector/vm_class_display_test.dart
+++ b/packages/devtools_app/test/vm_developer/object_inspector/vm_class_display_test.dart
@@ -2,9 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/config_specific/ide_theme/ide_theme.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_class_display.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_developer_common_widgets.dart';
+import 'package:devtools_app/src/shared/globals.dart';
import 'package:devtools_test/devtools_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -56,7 +57,8 @@
(WidgetTester tester) async {
await tester.pumpWidget(wrap(VmClassDisplay(clazz: mockClassObject)));
- expect(find.byType(ClassInfoWidget), findsOneWidget);
+ expect(find.byType(VmObjectDisplayBasicLayout), findsOneWidget);
+ expect(find.byType(VMInfoCard), findsOneWidget);
expect(find.text('General Information'), findsOneWidget);
expect(find.text('1 KB'), findsOneWidget);
expect(find.text('fooLib'), findsOneWidget);
@@ -73,6 +75,5 @@
expect(find.byType(InboundReferencesWidget), findsOneWidget);
// TODO(mtaylee): test ClassInstancesWidget when implemented
- // expect(find.byType(ClassInstancesWidget), findsOneWidget);
});
}
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/vm_code_display_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/vm_code_display_test.dart
index f283e30..917dfe5 100644
--- a/packages/devtools_app/test/vm_developer/object_inspector/vm_code_display_test.dart
+++ b/packages/devtools_app/test/vm_developer/object_inspector/vm_code_display_test.dart
@@ -4,9 +4,10 @@
import 'dart:math';
-import 'package:devtools_app/devtools_app.dart';
+import 'package:devtools_app/src/config_specific/ide_theme/ide_theme.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_code_display.dart';
import 'package:devtools_app/src/screens/vm_developer/vm_service_private_extensions.dart';
+import 'package:devtools_app/src/shared/globals.dart';
import 'package:devtools_app/src/shared/table.dart';
import 'package:devtools_test/devtools_test.dart';
import 'package:flutter/material.dart';
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/vm_developer_common_widgets_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/vm_developer_common_widgets_test.dart
index ff6c336..e6ea562 100644
--- a/packages/devtools_app/test/vm_developer/object_inspector/vm_developer_common_widgets_test.dart
+++ b/packages/devtools_app/test/vm_developer/object_inspector/vm_developer_common_widgets_test.dart
@@ -148,7 +148,7 @@
await tester.pumpAndSettle();
expect(find.byType(SelectableText), findsNWidgets(5));
- expect(find.text('fooClass'), findsOneWidget);
+ expect(find.text('FooClass'), findsOneWidget);
expect(
find.text('Retained by element [1] of <parentListName>'),
findsOneWidget,
diff --git a/packages/devtools_app/test/vm_developer/object_inspector/vm_field_display_test.dart b/packages/devtools_app/test/vm_developer/object_inspector/vm_field_display_test.dart
new file mode 100644
index 0000000..5143e31
--- /dev/null
+++ b/packages/devtools_app/test/vm_developer/object_inspector/vm_field_display_test.dart
@@ -0,0 +1,138 @@
+// Copyright 2022 The Chromium 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:devtools_app/src/config_specific/ide_theme/ide_theme.dart';
+import 'package:devtools_app/src/screens/vm_developer/vm_developer_common_widgets.dart';
+import 'package:devtools_app/src/screens/vm_developer/vm_field_display.dart';
+import 'package:devtools_app/src/screens/vm_developer/vm_service_private_extensions.dart';
+import 'package:devtools_app/src/shared/globals.dart';
+import 'package:devtools_test/devtools_test.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/mockito.dart';
+import 'package:vm_service/vm_service.dart';
+
+import '../vm_developer_test_utils.dart';
+
+void main() {
+ late MockFieldObject mockFieldObject;
+
+ const windowSize = Size(4000.0, 4000.0);
+
+ late Field testFieldCopy;
+
+ late InstanceRef fieldStaticValue;
+
+ setUp(() {
+ setGlobal(IdeTheme, IdeTheme());
+
+ mockFieldObject = MockFieldObject();
+
+ final fieldJson = testField.toJson();
+ testFieldCopy = Field.parse(fieldJson)!;
+
+ final instanceJson = testInstance.toJson();
+ fieldStaticValue = Instance.parse(instanceJson)!;
+
+ fieldStaticValue.name = 'FooNumberType';
+ fieldStaticValue.valueAsString = '100';
+
+ testFieldCopy.size = 256;
+ testFieldCopy.staticValue = fieldStaticValue;
+
+ when(mockFieldObject.outlineNode).thenReturn(null);
+ when(mockFieldObject.scriptRef).thenReturn(null);
+ when(mockFieldObject.name).thenReturn(testFieldCopy.name);
+ when(mockFieldObject.ref).thenReturn(testFieldCopy);
+ when(mockFieldObject.obj).thenReturn(testFieldCopy);
+ when(mockFieldObject.script).thenReturn(testScript);
+ when(mockFieldObject.pos).thenReturn(testPos);
+
+ when(mockFieldObject.guardClass).thenReturn(null);
+ when(mockFieldObject.guardNullable).thenReturn(null);
+ when(mockFieldObject.guardClassKind).thenReturn(null);
+
+ when(mockFieldObject.fetchingReachableSize)
+ .thenReturn(ValueNotifier<bool>(false));
+ when(mockFieldObject.reachableSize).thenReturn(testRequestableSize);
+ when(mockFieldObject.fetchingRetainedSize)
+ .thenReturn(ValueNotifier<bool>(false));
+ when(mockFieldObject.retainedSize).thenReturn(testRequestableSize);
+ when(mockFieldObject.retainingPath).thenReturn(
+ ValueNotifier<RetainingPath?>(testRetainingPath),
+ );
+ when(mockFieldObject.inboundReferences).thenReturn(
+ ValueNotifier<InboundReferences?>(testInboundRefs),
+ );
+ });
+
+ group('field data display tests', () {
+ testWidgetsWithWindowSize('basic layout', windowSize,
+ (WidgetTester tester) async {
+ await tester.pumpWidget(wrap(VmFieldDisplay(field: mockFieldObject)));
+
+ expect(find.byType(VmObjectDisplayBasicLayout), findsOneWidget);
+ expect(find.byType(VMInfoCard), findsOneWidget);
+ expect(find.text('General Information'), findsOneWidget);
+ expect(find.text('Field'), findsOneWidget);
+ expect(find.text('256 B'), findsOneWidget);
+ expect(find.text('Owner:'), findsOneWidget);
+ expect(find.text('fooLib'), findsOneWidget);
+ expect(find.text('fooScript.dart:10:4'), findsOneWidget);
+ expect(find.text('Observed types not found'), findsOneWidget);
+ expect(find.text('Static Value:'), findsOneWidget);
+ expect(find.text('FooNumberType: 100'), findsOneWidget);
+
+ expect(find.byType(RequestableSizeWidget), findsNWidgets(2));
+
+ expect(find.byType(RetainingPathWidget), findsOneWidget);
+
+ expect(find.byType(InboundReferencesWidget), findsOneWidget);
+ });
+
+ testWidgetsWithWindowSize('observed type single - nullable', windowSize,
+ (WidgetTester tester) async {
+ when(mockFieldObject.guardClass).thenReturn(testClass);
+ when(mockFieldObject.guardNullable).thenReturn(true);
+ when(mockFieldObject.guardClassKind).thenReturn(GuardClassKind.single);
+
+ await tester.pumpWidget(wrap(VmFieldDisplay(field: mockFieldObject)));
+ expect(find.text('FooClass - null observed'), findsOneWidget);
+ });
+
+ testWidgetsWithWindowSize(
+ 'observed type dynamic - non-nullable', windowSize,
+ (WidgetTester tester) async {
+ when(mockFieldObject.guardClass).thenReturn(null);
+ when(mockFieldObject.guardNullable).thenReturn(false);
+ when(mockFieldObject.guardClassKind).thenReturn(GuardClassKind.dynamic);
+
+ await tester.pumpWidget(wrap(VmFieldDisplay(field: mockFieldObject)));
+ expect(find.text('various - null not observed'), findsOneWidget);
+ });
+
+ testWidgetsWithWindowSize(
+ 'observed type unknown - null unknown', windowSize,
+ (WidgetTester tester) async {
+ when(mockFieldObject.guardClass).thenReturn(null);
+ when(mockFieldObject.guardNullable).thenReturn(null);
+ when(mockFieldObject.guardClassKind).thenReturn(GuardClassKind.unknown);
+
+ await tester.pumpWidget(wrap(VmFieldDisplay(field: mockFieldObject)));
+ expect(
+ find.text('none'),
+ findsOneWidget,
+ );
+ });
+
+ testWidgetsWithWindowSize('static value is not InstanceRef', windowSize,
+ (WidgetTester tester) async {
+ testFieldCopy.staticValue = testClass;
+
+ await tester.pumpWidget(wrap(VmFieldDisplay(field: mockFieldObject)));
+
+ expect(find.text('Static Value:'), findsNothing);
+ });
+ });
+}
diff --git a/packages/devtools_app/test/vm_developer/vm_developer_test_utils.dart b/packages/devtools_app/test/vm_developer/vm_developer_test_utils.dart
index 1259f25..d3c6805 100644
--- a/packages/devtools_app/test/vm_developer/vm_developer_test_utils.dart
+++ b/packages/devtools_app/test/vm_developer/vm_developer_test_utils.dart
@@ -19,7 +19,7 @@
);
final testClass = Class(
- name: 'fooClass',
+ name: 'FooClass',
library: testLib,
isAbstract: false,
isConst: false,
diff --git a/packages/devtools_test/lib/src/mocks/generated.dart b/packages/devtools_test/lib/src/mocks/generated.dart
index f79631b..511e040 100644
--- a/packages/devtools_test/lib/src/mocks/generated.dart
+++ b/packages/devtools_test/lib/src/mocks/generated.dart
@@ -27,6 +27,7 @@
ObjectGroupBase,
ClassObject,
CodeObject,
+ FieldObject,
ui.Image,
])
void main() {}