Do not send count and offset params when requesting the entire object (#5356)

diff --git a/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart b/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart
index 50955c6..c05ee47 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/dart_object_node.dart
@@ -241,13 +241,9 @@
 
     final value = this.value;
     if (value is InstanceRef) {
-      if (value.kind != null &&
-          (isList(value) ||
-              value.kind == InstanceKind.kMap ||
-              value.kind == InstanceKind.kRecord ||
-              isSet)) {
-        return value.length ?? 0;
-      }
+      final instanceLength = value.length;
+      if (instanceLength == null) return 0;
+      return instanceLength - offset;
     }
 
     return 0;
@@ -255,6 +251,20 @@
 
   int? _childCount;
 
+  bool get isPartialObject {
+    final value = this.value;
+    // Only instance kinds can be partial:
+    if (value is InstanceRef) {
+      // Only instance kinds with a length property can be partial. See:
+      // https://api.flutter.dev/flutter/vm_service/Instance/length.html
+      final instanceLength = value.length;
+      if (instanceLength == null) return false;
+      return offset != 0 || childCount < instanceLength;
+    }
+
+    return false;
+  }
+
   // TODO(elliette): Can remove this workaround once DWDS correctly returns
   // InstanceKind.kSet for the kind of `Sets`. See:
   // https://github.com/dart-lang/webdev/issues/2001
diff --git a/packages/devtools_app/lib/src/shared/diagnostics/helpers.dart b/packages/devtools_app/lib/src/shared/diagnostics/helpers.dart
index 3810983..f2de0e7 100644
--- a/packages/devtools_app/lib/src/shared/diagnostics/helpers.dart
+++ b/packages/devtools_app/lib/src/shared/diagnostics/helpers.dart
@@ -16,11 +16,22 @@
   required ObjRef value,
   DartObjectNode? variable,
 }) async {
+  // Don't include the offset and count parameters if we are not fetching a
+  // partial object. Offset and count parameters are only necessary to request
+  // subranges of the following instance kinds:
+  // https://api.flutter.dev/flutter/vm_service/VmServiceInterface/getObject.html
+  if (variable == null || !variable.isPartialObject) {
+    return await serviceManager.service!.getObject(
+      isolateRef!.id!,
+      value.id!,
+    );
+  }
+
   return await serviceManager.service!.getObject(
     isolateRef!.id!,
     value.id!,
-    offset: variable?.offset,
-    count: variable?.childCount,
+    offset: variable.offset,
+    count: variable.childCount,
   );
 }
 
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index 9ae7f4b..99e94e3 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -23,7 +23,7 @@
 TODO: Remove this section if there are not any general updates.
 
 ## Debugger updates
-TODO: Remove this section if there are not any general updates.
+* Fix a bug where variable inspection for instances sometimes showed no children. - [#5356](https://github.com/flutter/devtools/pull/5356)
 
 ## Network profiler updates
 TODO: Remove this section if there are not any general updates.
diff --git a/packages/devtools_app/test/debugger/association_variable_test.dart b/packages/devtools_app/test/debugger/association_variable_test.dart
index 790f16d..6ae05c0 100644
--- a/packages/devtools_app/test/debugger/association_variable_test.dart
+++ b/packages/devtools_app/test/debugger/association_variable_test.dart
@@ -65,7 +65,7 @@
         ),
         isolateRef,
       );
-      when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 2))
+      when(manager.service!.getObject(isolateId, objectId))
           .thenAnswer((_) async {
         return instance;
       });
@@ -113,7 +113,7 @@
         ),
         isolateRef,
       );
-      when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 2))
+      when(manager.service!.getObject(isolateId, objectId))
           .thenAnswer((_) async {
         return instance;
       });
@@ -161,7 +161,7 @@
         ),
         isolateRef,
       );
-      when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 2))
+      when(manager.service!.getObject(isolateId, objectId))
           .thenAnswer((_) async {
         return instance;
       });
diff --git a/packages/devtools_app/test/debugger/debugger_screen_variables_test.dart b/packages/devtools_app/test/debugger/debugger_screen_variables_test.dart
index 75fec88..67c7f41 100644
--- a/packages/devtools_app/test/debugger/debugger_screen_variables_test.dart
+++ b/packages/devtools_app/test/debugger/debugger_screen_variables_test.dart
@@ -7,7 +7,6 @@
 import 'package:devtools_app/src/screens/debugger/debugger_screen.dart';
 import 'package:devtools_app/src/service/service_manager.dart';
 import 'package:devtools_app/src/shared/config_specific/ide_theme/ide_theme.dart';
-import 'package:devtools_app/src/shared/diagnostics/dart_object_node.dart';
 import 'package:devtools_app/src/shared/diagnostics/tree_builder.dart';
 import 'package:devtools_app/src/shared/globals.dart';
 import 'package:devtools_app/src/shared/notifications.dart';
@@ -16,7 +15,8 @@
 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 '../test_infra/utils/variable_utils.dart';
 
 void main() {
   late FakeServiceManager fakeServiceManager;
@@ -45,8 +45,8 @@
         .thenReturn(ValueNotifier<int>(0));
     debuggerController = createMockDebuggerControllerWithDefaults();
 
-    _resetRef();
-    _resetRoot();
+    resetRef();
+    resetRoot();
   });
 
   Future<void> pumpDebuggerScreen(
@@ -61,16 +61,61 @@
     );
   }
 
+  Future<void> verifyGroupings(
+    WidgetTester tester, {
+    required Finder parentFinder,
+  }) async {
+    final group0To9999Finder = find.selectableTextContaining('[0 - 9999]');
+    final group10000To19999Finder =
+        find.selectableTextContaining('[10000 - 19999]');
+    final group230000To239999Finder =
+        find.selectableTextContaining('[230000 - 239999]');
+    final group240000To243620Finder =
+        find.selectableTextContaining('[240000 - 243620]');
+
+    final group0To99Finder = find.selectableTextContaining('[0 - 99]');
+    final group100To199Finder = find.selectableTextContaining('[100 - 199]');
+    final group200To299Finder = find.selectableTextContaining('[200 - 299]');
+
+    // Initially the parent variable is not expanded.
+    expect(parentFinder, findsOneWidget);
+    expect(group0To9999Finder, findsNothing);
+    expect(group10000To19999Finder, findsNothing);
+    expect(group230000To239999Finder, findsNothing);
+    expect(group240000To243620Finder, findsNothing);
+
+    // Expand the parent variable.
+    await tester.tap(parentFinder);
+    await tester.pump();
+    expect(group0To9999Finder, findsOneWidget);
+    expect(group10000To19999Finder, findsOneWidget);
+    expect(group230000To239999Finder, findsOneWidget);
+    expect(group240000To243620Finder, findsOneWidget);
+
+    // Initially group [0 - 9999] is not expanded.
+    expect(group0To99Finder, findsNothing);
+    expect(group100To199Finder, findsNothing);
+    expect(group200To299Finder, findsNothing);
+
+    // Expand group [0 - 9999].
+    await tester.tap(group0To9999Finder);
+    await tester.pump();
+    expect(group0To99Finder, findsOneWidget);
+    expect(group100To199Finder, findsOneWidget);
+    expect(group200To299Finder, findsOneWidget);
+  }
+
   testWidgetsWithWindowSize(
     'Variables shows items',
     windowSize,
     (WidgetTester tester) async {
       fakeServiceManager.appState.setVariables(
         [
-          _buildListVariable(),
-          _buildMapVariable(),
-          _buildStringVariable('test str'),
-          _buildBooleanVariable(true),
+          buildListVariable(),
+          buildMapVariable(),
+          buildStringVariable('test str'),
+          buildBooleanVariable(true),
+          buildSetVariable(),
         ],
       );
       await pumpDebuggerScreen(tester, debuggerController);
@@ -120,6 +165,20 @@
       await tester.pump();
       expect(mapElement1Finder, findsOneWidget);
       expect(mapElement2Finder, findsOneWidget);
+
+      // Expect a tooltip for the set instance.
+      final setFinder = find.selectableText('Root 5: Set (2 items)');
+      expect(setFinder, findsOneWidget);
+
+      // Initially set is not expanded.
+      expect(find.selectableTextContaining('set value 0'), findsNothing);
+      expect(find.selectableTextContaining('set value 1'), findsNothing);
+
+      // Expand set
+      await tester.tap(setFinder);
+      await tester.pump();
+      expect(find.selectableTextContaining('set value 0'), findsOneWidget);
+      expect(find.selectableTextContaining('set value 1'), findsOneWidget);
     },
   );
 
@@ -127,7 +186,7 @@
     'Children in large list variables are grouped',
     windowSize,
     (WidgetTester tester) async {
-      final list = _buildParentListVariable(length: 380250);
+      final list = buildParentListVariable(length: 243621);
       await buildVariablesTree(list);
 
       final appState = serviceManager.appState;
@@ -135,48 +194,8 @@
 
       await pumpDebuggerScreen(tester, debuggerController);
 
-      final listFinder = find.selectableText('Root 1: List (380,250 items)');
-      final group0To9999Finder = find.selectableTextContaining('[0 - 9999]');
-      final group10000To19999Finder =
-          find.selectableTextContaining('[10000 - 19999]');
-      final group370000To379999Finder =
-          find.selectableTextContaining('[370000 - 379999]');
-      final group380000To380249Finder =
-          find.selectableTextContaining('[380000 - 380249]');
-
-      final group370000To370099Finder =
-          find.selectableTextContaining('[370000 - 370099]');
-      final group370100To370199Finder =
-          find.selectableTextContaining('[370100 - 370199]');
-      final group370200To370299Finder =
-          find.selectableTextContaining('[370200 - 370299]');
-
-      // Initially list is not expanded.
-      expect(listFinder, findsOneWidget);
-      expect(group0To9999Finder, findsNothing);
-      expect(group10000To19999Finder, findsNothing);
-      expect(group370000To379999Finder, findsNothing);
-      expect(group380000To380249Finder, findsNothing);
-
-      // Expand list.
-      await tester.tap(listFinder);
-      await tester.pump();
-      expect(group0To9999Finder, findsOneWidget);
-      expect(group10000To19999Finder, findsOneWidget);
-      expect(group370000To379999Finder, findsOneWidget);
-      expect(group380000To380249Finder, findsOneWidget);
-
-      // Initially group [370000 - 379999] is not expanded.
-      expect(group370000To370099Finder, findsNothing);
-      expect(group370100To370199Finder, findsNothing);
-      expect(group370200To370299Finder, findsNothing);
-
-      // Expand group [370000 - 379999].
-      await tester.tap(group370000To379999Finder);
-      await tester.pump();
-      expect(group370000To370099Finder, findsOneWidget);
-      expect(group370100To370199Finder, findsOneWidget);
-      expect(group370200To370299Finder, findsOneWidget);
+      final listFinder = find.selectableText('Root 1: List (243,621 items)');
+      await verifyGroupings(tester, parentFinder: listFinder);
     },
   );
 
@@ -184,7 +203,7 @@
     'Children in large map variables are grouped',
     windowSize,
     (WidgetTester tester) async {
-      final map = _buildParentMapVariable(length: 243621);
+      final map = buildParentMapVariable(length: 243621);
       await buildVariablesTree(map);
 
       final appState = serviceManager.appState;
@@ -192,214 +211,25 @@
 
       await pumpDebuggerScreen(tester, debuggerController);
 
-      final listFinder = find.selectableText('Root 1: Map (243,621 items)');
-      final group0To9999Finder = find.selectableTextContaining('[0 - 9999]');
-      final group10000To19999Finder =
-          find.selectableTextContaining('[10000 - 19999]');
-      final group230000To239999Finder =
-          find.selectableTextContaining('[230000 - 239999]');
-      final group240000To243620Finder =
-          find.selectableTextContaining('[240000 - 243620]');
-
-      final group0To99Finder = find.selectableTextContaining('[0 - 99]');
-      final group100To199Finder = find.selectableTextContaining('[100 - 199]');
-      final group200To299Finder = find.selectableTextContaining('[200 - 299]');
-
-      // Initially map is not expanded.
-      expect(listFinder, findsOneWidget);
-      expect(group0To9999Finder, findsNothing);
-      expect(group10000To19999Finder, findsNothing);
-      expect(group230000To239999Finder, findsNothing);
-      expect(group240000To243620Finder, findsNothing);
-
-      // Expand map.
-      await tester.tap(listFinder);
-      await tester.pump();
-      expect(group0To9999Finder, findsOneWidget);
-      expect(group10000To19999Finder, findsOneWidget);
-      expect(group230000To239999Finder, findsOneWidget);
-      expect(group240000To243620Finder, findsOneWidget);
-
-      // Initially group [0 - 9999] is not expanded.
-      expect(group0To99Finder, findsNothing);
-      expect(group100To199Finder, findsNothing);
-      expect(group200To299Finder, findsNothing);
-
-      // Expand group [0 - 9999].
-      await tester.tap(group0To9999Finder);
-      await tester.pump();
-      expect(group0To99Finder, findsOneWidget);
-      expect(group100To199Finder, findsOneWidget);
-      expect(group200To299Finder, findsOneWidget);
+      final mapFinder = find.selectableText('Root 1: Map (243,621 items)');
+      await verifyGroupings(tester, parentFinder: mapFinder);
     },
   );
-}
 
-final _libraryRef = LibraryRef(
-  name: 'some library',
-  uri: 'package:foo/foo.dart',
-  id: 'lib-id-1',
-);
+  testWidgetsWithWindowSize(
+    'Children in large set variables are grouped',
+    windowSize,
+    (WidgetTester tester) async {
+      final set = buildParentSetVariable(length: 243621);
+      await buildVariablesTree(set);
 
-final _isolateRef = IsolateRef(
-  id: '433',
-  number: '1',
-  name: 'my-isolate',
-  isSystemIsolate: false,
-);
+      final appState = serviceManager.appState;
+      appState.setVariables([set]);
 
-int _refNumber = 0;
+      await pumpDebuggerScreen(tester, debuggerController);
 
-String _incrementRef() {
-  _refNumber++;
-  return 'ref$_refNumber';
-}
-
-void _resetRef() {
-  _refNumber = 0;
-}
-
-int _rootNumber = 0;
-
-String _incrementRoot() {
-  _rootNumber++;
-  return 'Root $_rootNumber';
-}
-
-void _resetRoot() {
-  _rootNumber = 0;
-}
-
-DartObjectNode _buildParentListVariable({int length = 2}) {
-  return DartObjectNode.create(
-    BoundVariable(
-      name: _incrementRoot(),
-      value: InstanceRef(
-        id: _incrementRef(),
-        kind: InstanceKind.kList,
-        classRef: ClassRef(
-          name: '_GrowableList',
-          id: _incrementRef(),
-          library: _libraryRef,
-        ),
-        length: length,
-      ),
-    ),
-    _isolateRef,
-  );
-}
-
-DartObjectNode _buildListVariable({int length = 2}) {
-  final listVariable = _buildParentListVariable(length: length);
-
-  for (int i = 0; i < length; i++) {
-    listVariable.addChild(
-      DartObjectNode.create(
-        BoundVariable(
-          name: '$i',
-          value: InstanceRef(
-            id: _incrementRef(),
-            kind: InstanceKind.kInt,
-            classRef: ClassRef(
-              name: 'Integer',
-              id: _incrementRef(),
-              library: _libraryRef,
-            ),
-            valueAsString: '$i',
-            valueAsStringIsTruncated: false,
-          ),
-        ),
-        _isolateRef,
-      ),
-    );
-  }
-
-  return listVariable;
-}
-
-DartObjectNode _buildParentMapVariable({int length = 2}) {
-  return DartObjectNode.create(
-    BoundVariable(
-      name: _incrementRoot(),
-      value: InstanceRef(
-        id: _incrementRef(),
-        kind: InstanceKind.kMap,
-        classRef: ClassRef(
-          name: '_InternalLinkedHashmap',
-          id: _incrementRef(),
-          library: _libraryRef,
-        ),
-        length: length,
-      ),
-    ),
-    _isolateRef,
-  );
-}
-
-DartObjectNode _buildMapVariable({int length = 2}) {
-  final mapVariable = _buildParentMapVariable(length: length);
-
-  for (int i = 0; i < length; i++) {
-    mapVariable.addChild(
-      DartObjectNode.create(
-        BoundVariable(
-          name: "['key${i + 1}']",
-          value: InstanceRef(
-            id: _incrementRef(),
-            kind: InstanceKind.kDouble,
-            classRef: ClassRef(
-              name: 'Double',
-              id: _incrementRef(),
-              library: _libraryRef,
-            ),
-            valueAsString: '${i + 1}.0',
-            valueAsStringIsTruncated: false,
-          ),
-        ),
-        _isolateRef,
-      ),
-    );
-  }
-
-  return mapVariable;
-}
-
-DartObjectNode _buildStringVariable(String value) {
-  return DartObjectNode.create(
-    BoundVariable(
-      name: _incrementRoot(),
-      value: InstanceRef(
-        id: _incrementRef(),
-        kind: InstanceKind.kString,
-        classRef: ClassRef(
-          name: 'String',
-          id: _incrementRef(),
-          library: _libraryRef,
-        ),
-        valueAsString: value,
-        valueAsStringIsTruncated: true,
-      ),
-    ),
-    _isolateRef,
-  );
-}
-
-DartObjectNode _buildBooleanVariable(bool value) {
-  return DartObjectNode.create(
-    BoundVariable(
-      name: _incrementRoot(),
-      value: InstanceRef(
-        id: _incrementRef(),
-        kind: InstanceKind.kBool,
-        classRef: ClassRef(
-          name: 'Boolean',
-          id: _incrementRef(),
-          library: _libraryRef,
-        ),
-        valueAsString: '$value',
-        valueAsStringIsTruncated: false,
-      ),
-    ),
-    _isolateRef,
+      final setFinder = find.selectableText('Root 1: Set (243,621 items)');
+      await verifyGroupings(tester, parentFinder: setFinder);
+    },
   );
 }
diff --git a/packages/devtools_app/test/debugger/records_variable_test.dart b/packages/devtools_app/test/debugger/records_variable_test.dart
index 9497ef4..2e7954b 100644
--- a/packages/devtools_app/test/debugger/records_variable_test.dart
+++ b/packages/devtools_app/test/debugger/records_variable_test.dart
@@ -82,8 +82,6 @@
         manager.service!.getObject(
           isolateId,
           objectId,
-          offset: 0,
-          count: 3,
         ),
       ).thenAnswer((_) async {
         return instance;
diff --git a/packages/devtools_app/test/debugger/typed_data_variable_test.dart b/packages/devtools_app/test/debugger/typed_data_variable_test.dart
index f64f7e0..b7c80f2 100644
--- a/packages/devtools_app/test/debugger/typed_data_variable_test.dart
+++ b/packages/devtools_app/test/debugger/typed_data_variable_test.dart
@@ -51,8 +51,7 @@
       isolateRef,
     );
 
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -81,8 +80,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -111,8 +109,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -141,8 +138,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -174,7 +170,7 @@
         ),
         isolateRef,
       );
-      when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
+      when(manager.service!.getObject(isolateId, objectId))
           .thenAnswer((_) async {
         return instance;
       });
@@ -206,8 +202,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -236,8 +231,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -266,8 +260,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -299,7 +292,7 @@
         ),
         isolateRef,
       );
-      when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
+      when(manager.service!.getObject(isolateId, objectId))
           .thenAnswer((_) async {
         return instance;
       });
@@ -332,8 +325,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -362,8 +354,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -393,8 +384,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
     await buildVariablesTree(variable);
@@ -430,8 +420,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
@@ -465,8 +454,7 @@
       ),
       isolateRef,
     );
-    when(manager.service!.getObject(isolateId, objectId, offset: 0, count: 4))
-        .thenAnswer((_) async {
+    when(manager.service!.getObject(isolateId, objectId)).thenAnswer((_) async {
       return instance;
     });
 
diff --git a/packages/devtools_app/test/shared/dart_object_node_test.dart b/packages/devtools_app/test/shared/dart_object_node_test.dart
new file mode 100644
index 0000000..3aa661c
--- /dev/null
+++ b/packages/devtools_app/test/shared/dart_object_node_test.dart
@@ -0,0 +1,108 @@
+// Copyright 2023 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_test/flutter_test.dart';
+
+import '../test_infra/utils/variable_utils.dart';
+
+void main() {
+  setUp(() {
+    resetRef();
+    resetRoot();
+  });
+
+  group('map instances', () {
+    test('entire map', () {
+      final map = buildMapVariable(length: 3);
+      expect(map.childCount, equals(3));
+      expect(map.isPartialObject, isFalse);
+    });
+
+    test('map grouping with offset', () {
+      final mapGrouping = buildMapGroupingVariable(
+        length: 10,
+        offset: 2,
+        count: 4,
+      );
+      expect(mapGrouping.childCount, equals(4));
+      expect(mapGrouping.isPartialObject, isTrue);
+    });
+
+    test('map grouping no offset', () {
+      final mapGrouping = buildMapGroupingVariable(
+        length: 10,
+        offset: 0,
+        count: 4,
+      );
+      expect(mapGrouping.childCount, equals(4));
+      expect(mapGrouping.isPartialObject, isTrue);
+    });
+  });
+
+  group('list instances', () {
+    test('entire list', () {
+      final list = buildListVariable(length: 3);
+      expect(list.childCount, equals(3));
+      expect(list.isPartialObject, isFalse);
+    });
+    test('list grouping with offset', () {
+      final listGrouping = buildListGroupingVariable(
+        length: 10,
+        offset: 2,
+        count: 4,
+      );
+      expect(listGrouping.childCount, equals(4));
+      expect(listGrouping.isPartialObject, isTrue);
+    });
+    test('list grouping no offset', () {
+      final listGrouping = buildListGroupingVariable(
+        length: 10,
+        offset: 0,
+        count: 4,
+      );
+      expect(listGrouping.childCount, equals(4));
+      expect(listGrouping.isPartialObject, isTrue);
+    });
+  });
+
+  group('set instances', () {
+    test('entire set', () {
+      final set = buildSetVariable(length: 3);
+      expect(set.childCount, equals(3));
+      expect(set.isPartialObject, isFalse);
+    });
+
+    test('set grouping with offset', () {
+      final setGrouping = buildSetGroupingVariable(
+        length: 10,
+        offset: 2,
+        count: 4,
+      );
+      expect(setGrouping.childCount, equals(4));
+      expect(setGrouping.isPartialObject, isTrue);
+    });
+
+    test('set grouping no offset', () {
+      final setGrouping = buildSetGroupingVariable(
+        length: 10,
+        offset: 0,
+        count: 4,
+      );
+      expect(setGrouping.childCount, equals(4));
+      expect(setGrouping.isPartialObject, isTrue);
+    });
+  });
+
+  test('booleans', () {
+    final boolean = buildBooleanVariable(true);
+    expect(boolean.childCount, equals(0));
+    expect(boolean.isPartialObject, isFalse);
+  });
+
+  test('strings', () {
+    final str = buildStringVariable('Hello there!');
+    expect(str.childCount, equals(0));
+    expect(str.isPartialObject, isFalse);
+  });
+}
diff --git a/packages/devtools_app/test/test_infra/utils/variable_utils.dart b/packages/devtools_app/test/test_infra/utils/variable_utils.dart
new file mode 100644
index 0000000..35fb7d2
--- /dev/null
+++ b/packages/devtools_app/test/test_infra/utils/variable_utils.dart
@@ -0,0 +1,274 @@
+// Copyright 2023 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/shared/diagnostics/dart_object_node.dart';
+import 'package:devtools_app/src/shared/diagnostics/generic_instance_reference.dart';
+
+import 'package:vm_service/vm_service.dart';
+
+import '../../debugger/typed_data_variable_test.dart';
+
+void resetRef() {
+  _refNumber = 0;
+}
+
+void resetRoot() {
+  _rootNumber = 0;
+}
+
+DartObjectNode buildParentListVariable({int length = 2}) {
+  return DartObjectNode.create(
+    BoundVariable(
+      name: _incrementRoot(),
+      value: _buildInstanceRefForList(length: length),
+    ),
+    _isolateRef,
+  );
+}
+
+DartObjectNode buildListVariable({int length = 2}) {
+  final listVariable = buildParentListVariable(length: length);
+
+  for (int i = 0; i < length; i++) {
+    listVariable.addChild(
+      DartObjectNode.create(
+        BoundVariable(
+          name: '$i',
+          value: InstanceRef(
+            id: _incrementRef(),
+            kind: InstanceKind.kInt,
+            classRef: ClassRef(
+              name: 'Integer',
+              id: _incrementRef(),
+              library: _libraryRef,
+            ),
+            valueAsString: '$i',
+            valueAsStringIsTruncated: false,
+          ),
+        ),
+        _isolateRef,
+      ),
+    );
+  }
+
+  return listVariable;
+}
+
+DartObjectNode buildListGroupingVariable({
+  required int length,
+  required int offset,
+  required int count,
+}) {
+  return DartObjectNode.grouping(
+    GenericInstanceRef(
+      isolateRef: isolateRef,
+      value: _buildInstanceRefForList(length: length),
+    ),
+    offset: offset,
+    count: count,
+  );
+}
+
+DartObjectNode buildParentMapVariable({int length = 2}) {
+  return DartObjectNode.create(
+    BoundVariable(
+      name: _incrementRoot(),
+      value: _buildInstanceRefForMap(length: length),
+    ),
+    _isolateRef,
+  );
+}
+
+DartObjectNode buildMapVariable({int length = 2}) {
+  final mapVariable = buildParentMapVariable(length: length);
+
+  for (int i = 0; i < length; i++) {
+    mapVariable.addChild(
+      DartObjectNode.create(
+        BoundVariable(
+          name: "['key${i + 1}']",
+          value: InstanceRef(
+            id: _incrementRef(),
+            kind: InstanceKind.kDouble,
+            classRef: ClassRef(
+              name: 'Double',
+              id: _incrementRef(),
+              library: _libraryRef,
+            ),
+            valueAsString: '${i + 1}.0',
+            valueAsStringIsTruncated: false,
+          ),
+        ),
+        _isolateRef,
+      ),
+    );
+  }
+
+  return mapVariable;
+}
+
+DartObjectNode buildMapGroupingVariable({
+  required int length,
+  required int offset,
+  required int count,
+}) {
+  return DartObjectNode.grouping(
+    GenericInstanceRef(
+      isolateRef: isolateRef,
+      value: _buildInstanceRefForMap(length: length),
+    ),
+    offset: offset,
+    count: count,
+  );
+}
+
+DartObjectNode buildParentSetVariable({int length = 2}) {
+  return DartObjectNode.create(
+    BoundVariable(
+      name: _incrementRoot(),
+      value: _buildInstanceRefForSet(length: length),
+    ),
+    _isolateRef,
+  );
+}
+
+DartObjectNode buildSetVariable({int length = 2}) {
+  final setVariable = buildParentSetVariable(length: length);
+
+  for (int i = 0; i < length; i++) {
+    setVariable.addChild(
+      DartObjectNode.fromValue(
+        value: InstanceRef(
+          id: _incrementRef(),
+          kind: InstanceKind.kString,
+          classRef: ClassRef(
+            name: 'String',
+            id: _incrementRef(),
+            library: _libraryRef,
+          ),
+          valueAsString: 'set value $i',
+          valueAsStringIsTruncated: false,
+        ),
+        isolateRef: _isolateRef,
+      ),
+    );
+  }
+
+  return setVariable;
+}
+
+DartObjectNode buildSetGroupingVariable({
+  required int length,
+  required int offset,
+  required int count,
+}) {
+  return DartObjectNode.grouping(
+    GenericInstanceRef(
+      isolateRef: isolateRef,
+      value: _buildInstanceRefForSet(length: length),
+    ),
+    offset: offset,
+    count: count,
+  );
+}
+
+DartObjectNode buildStringVariable(String value) {
+  return DartObjectNode.create(
+    BoundVariable(
+      name: _incrementRoot(),
+      value: InstanceRef(
+        id: _incrementRef(),
+        kind: InstanceKind.kString,
+        classRef: ClassRef(
+          name: 'String',
+          id: _incrementRef(),
+          library: _libraryRef,
+        ),
+        valueAsString: value,
+        valueAsStringIsTruncated: true,
+      ),
+    ),
+    _isolateRef,
+  );
+}
+
+DartObjectNode buildBooleanVariable(bool value) {
+  return DartObjectNode.create(
+    BoundVariable(
+      name: _incrementRoot(),
+      value: InstanceRef(
+        id: _incrementRef(),
+        kind: InstanceKind.kBool,
+        classRef: ClassRef(
+          name: 'Boolean',
+          id: _incrementRef(),
+          library: _libraryRef,
+        ),
+        valueAsString: '$value',
+        valueAsStringIsTruncated: false,
+      ),
+    ),
+    _isolateRef,
+  );
+}
+
+InstanceRef _buildInstanceRefForMap({required int length}) => InstanceRef(
+      id: _incrementRef(),
+      kind: InstanceKind.kMap,
+      classRef: ClassRef(
+        name: '_InternalLinkedHashmap',
+        id: _incrementRef(),
+        library: _libraryRef,
+      ),
+      length: length,
+    );
+
+InstanceRef _buildInstanceRefForList({required int length}) => InstanceRef(
+      id: _incrementRef(),
+      kind: InstanceKind.kList,
+      classRef: ClassRef(
+        name: '_GrowableList',
+        id: _incrementRef(),
+        library: _libraryRef,
+      ),
+      length: length,
+    );
+
+InstanceRef _buildInstanceRefForSet({required int length}) => InstanceRef(
+      id: _incrementRef(),
+      kind: InstanceKind.kSet,
+      classRef: ClassRef(
+        name: '_Set',
+        id: _incrementRef(),
+        library: _libraryRef,
+      ),
+      length: length,
+    );
+
+final _libraryRef = LibraryRef(
+  name: 'some library',
+  uri: 'package:foo/foo.dart',
+  id: 'lib-id-1',
+);
+
+final _isolateRef = IsolateRef(
+  id: '433',
+  number: '1',
+  name: 'my-isolate',
+  isSystemIsolate: false,
+);
+
+int _refNumber = 0;
+
+String _incrementRef() {
+  _refNumber++;
+  return 'ref$_refNumber';
+}
+
+int _rootNumber = 0;
+
+String _incrementRoot() {
+  _rootNumber++;
+  return 'Root $_rootNumber';
+}