| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:developer'; |
| |
| import 'package:test/test.dart'; |
| import 'package:vm_service/vm_service.dart'; |
| |
| import 'common/service_test_common.dart'; |
| import 'common/test_helper.dart'; |
| |
| // AUTOGENERATED START |
| // |
| // Update these constants by running: |
| // |
| // dart pkg/vm_service/test/update_line_numbers.dart pkg/vm_service/test/id_zones_test.dart |
| // |
| const LINE_A = 34; |
| // AUTOGENERATED END |
| |
| class D {} |
| |
| @pragma('vm:entry-point') |
| D getDLiteral() => D(); |
| |
| class C { |
| final field = D(); |
| } |
| |
| void testeeMain() { |
| // ignore: unused_local_variable |
| final c = C(); |
| debugger(); // LINE_A |
| } |
| |
| final tests = <IsolateTest>[ |
| hasStoppedAtBreakpoint, |
| stoppedAtLine(LINE_A), |
| // Test the behaviour of an ID Zone with a `backingBufferKind` of `Ring`, an |
| // `idAssignmentPolicy` of `AlwaysAllocate`, and the default capacity. |
| (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final idZone1 = await service.createIdZone( |
| isolateId, |
| IdZoneBackingBufferKind.kRing, |
| IdAssignmentPolicy.kAlwaysAllocate, |
| ); |
| expect(idZone1.id, 'zones/1'); |
| expect(idZone1.backingBufferKind, IdZoneBackingBufferKind.kRing); |
| expect(idZone1.idAssignmentPolicy, IdAssignmentPolicy.kAlwaysAllocate); |
| |
| final cInstanceRef1 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone1.id, |
| ) as InstanceRef; |
| final cObjectId1 = cInstanceRef1.id!; |
| expect(cObjectId1, 'objects/0/1'); |
| |
| final cInstance1 = await service.getObject(isolateId, cObjectId1); |
| expect(cInstance1.type, 'Instance'); |
| |
| final cInstanceRef2 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone1.id, |
| ) as InstanceRef; |
| final cObjectId2 = cInstanceRef2.id!; |
| expect(cObjectId2, 'objects/1/1'); |
| |
| final cInstance2 = await service.getObject(isolateId, cObjectId2); |
| expect(cInstance2.type, 'Instance'); |
| |
| await service.invalidateIdZone( |
| isolateId, |
| idZone1.id!, |
| ); |
| |
| try { |
| await service.getObject(isolateId, cObjectId1); |
| fail('successfully retrieved object using expired ID'); |
| } on SentinelException catch (e) { |
| expect(e.sentinel.kind, startsWith('Expired')); |
| expect(e.sentinel.valueAsString, equals('<expired>')); |
| } |
| |
| // Ensure that the zone can be reused after it was invalidated. |
| final cInstanceRef3 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone1.id, |
| ) as InstanceRef; |
| expect(cInstanceRef3.id, 'objects/0/1'); |
| }, |
| |
| // Test the behaviour of an ID Zone with a `backingBufferKind` of `Ring`, an |
| // `idAssignmentPolicy` of `AlwaysAllocate`, and a capacity of 1. |
| (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final idZone2 = await service.createIdZone( |
| isolateId, |
| IdZoneBackingBufferKind.kRing, |
| IdAssignmentPolicy.kAlwaysAllocate, |
| capacity: 1, |
| ); |
| expect(idZone2.id, 'zones/2'); |
| expect(idZone2.backingBufferKind, IdZoneBackingBufferKind.kRing); |
| expect(idZone2.idAssignmentPolicy, IdAssignmentPolicy.kAlwaysAllocate); |
| |
| final cInstanceRef1 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone2.id, |
| ) as InstanceRef; |
| final cObjectId1 = cInstanceRef1.id!; |
| expect(cObjectId1, 'objects/0/2'); |
| |
| final cInstance1 = await service.getObject(isolateId, cObjectId1); |
| expect(cInstance1.type, 'Instance'); |
| |
| final cInstanceRef2 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone2.id, |
| ) as InstanceRef; |
| final cObjectId2 = cInstanceRef2.id!; |
| expect(cObjectId2, 'objects/1/2'); |
| |
| final cInstance2 = await service.getObject(isolateId, cObjectId2); |
| expect(cInstance2.type, 'Instance'); |
| |
| // [idZone2] only has a capacity of 1, so [cObjectId1] should have been |
| // evicted when [cObjectId2] was allocated. |
| try { |
| await service.getObject(isolateId, cObjectId1); |
| fail('successfully retrieved object using expired ID'); |
| } on SentinelException catch (e) { |
| expect(e.sentinel.kind, startsWith('Expired')); |
| expect(e.sentinel.valueAsString, equals('<expired>')); |
| } |
| }, |
| |
| // Test the behaviour of an ID Zone with a `backingBufferKind` of `Ring`, an |
| // `idAssignmentPolicy` of `ReuseExisting`, and the default capacity. |
| (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final idZone3 = await service.createIdZone( |
| isolateId, |
| IdZoneBackingBufferKind.kRing, |
| IdAssignmentPolicy.kReuseExisting, |
| ); |
| expect(idZone3.id, 'zones/3'); |
| expect(idZone3.backingBufferKind, IdZoneBackingBufferKind.kRing); |
| expect(idZone3.idAssignmentPolicy, IdAssignmentPolicy.kReuseExisting); |
| |
| final cInstanceRef1 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone3.id, |
| ) as InstanceRef; |
| final cObjectId1 = cInstanceRef1.id!; |
| expect(cObjectId1, 'objects/0/3'); |
| |
| final cInstance1 = await service.getObject(isolateId, cObjectId1); |
| expect(cInstance1.type, 'Instance'); |
| |
| final cInstanceRef2 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone3.id, |
| ) as InstanceRef; |
| final cObjectId2 = cInstanceRef2.id!; |
| expect(cObjectId2, 'objects/0/3'); |
| |
| final cInstance2 = await service.getObject(isolateId, cObjectId2); |
| expect(cInstance2.type, 'Instance'); |
| |
| await service.invalidateIdZone( |
| isolateId, |
| idZone3.id!, |
| ); |
| |
| try { |
| await service.getObject(isolateId, cObjectId1); |
| fail('successfully retrieved object using expired ID'); |
| } on SentinelException catch (e) { |
| expect(e.sentinel.kind, startsWith('Expired')); |
| expect(e.sentinel.valueAsString, equals('<expired>')); |
| } |
| |
| // Ensure that the zone can be reused after it was invalidated. |
| final cInstanceRef3 = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone3.id, |
| ) as InstanceRef; |
| expect(cInstanceRef3.id, 'objects/0/3'); |
| }, |
| |
| // Test deleting an ID Zone. |
| (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final idZone4 = await service.createIdZone( |
| isolateId, |
| IdZoneBackingBufferKind.kRing, |
| IdAssignmentPolicy.kAlwaysAllocate, |
| ); |
| expect(idZone4.id, 'zones/4'); |
| expect(idZone4.backingBufferKind, IdZoneBackingBufferKind.kRing); |
| expect(idZone4.idAssignmentPolicy, IdAssignmentPolicy.kAlwaysAllocate); |
| |
| await service.deleteIdZone( |
| isolateId, |
| idZone4.id!, |
| ); |
| |
| try { |
| await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c', |
| idZoneId: idZone4.id, |
| ); |
| fail('successfully used an ID zone that should have been deleted'); |
| } on RPCError catch (e) { |
| expect(e.code, RPCErrorKind.kInvalidParams.code); |
| } |
| }, |
| |
| // Test the `idZoneId` parameters of all RPCs that have them. |
| (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final idZone5 = await service.createIdZone( |
| isolateId, |
| IdZoneBackingBufferKind.kRing, |
| IdAssignmentPolicy.kAlwaysAllocate, |
| capacity: 30, |
| ); |
| expect(idZone5.id, 'zones/5'); |
| expect(idZone5.backingBufferKind, IdZoneBackingBufferKind.kRing); |
| expect(idZone5.idAssignmentPolicy, IdAssignmentPolicy.kAlwaysAllocate); |
| |
| // The `idZoneId` parameter of [VmService.evaluateInFrame] is already tested |
| // by the tests above. |
| |
| // Test the `idZoneId` parameter of [VmService.evaluate]. |
| |
| final isolate = await service.getIsolate(isolateId); |
| final dLiteralInstanceRef = await service.evaluate( |
| isolateId, |
| isolate.rootLib!.id!, |
| 'getDLiteral()', |
| idZoneId: idZone5.id, |
| ) as InstanceRef; |
| final dLiteralObjectId = dLiteralInstanceRef.id!; |
| expect(dLiteralObjectId, 'objects/0/5'); |
| |
| final dLiteralInstance = |
| await service.getObject(isolateId, dLiteralObjectId); |
| expect(dLiteralInstance.type, 'Instance'); |
| final dClassRef = dLiteralInstance.classRef!; |
| |
| // Test the `idZoneId` parameter of [VmService.getInstances]. |
| |
| final dInstanceSet = await service.getInstances( |
| isolateId, |
| dClassRef.id!, |
| 1, |
| idZoneId: idZone5.id, |
| ); |
| expect(dInstanceSet.totalCount, 1); |
| expect(dInstanceSet.instances!.first.id, 'objects/1/5'); |
| |
| // Test the `idZoneId` parameters of [VmService.getInstancesAsList] and |
| // [VmService.getObject]. |
| |
| final listInstanceRef = await service.getInstancesAsList( |
| isolateId, |
| dClassRef.id!, |
| idZoneId: idZone5.id, |
| ); |
| expect(listInstanceRef.length, 1); |
| final listId = listInstanceRef.id!; |
| expect(listId, 'objects/3/5'); |
| |
| final listInstance = await service.getObject( |
| isolateId, |
| listId, |
| idZoneId: idZone5.id, |
| ) as Instance; |
| expect(listInstance.elements!.first.id, 'objects/6/5'); |
| |
| // Test the `idZoneId` parameter of [VmService.getInboundReferences]. |
| |
| final dWithinCInstanceRef = await service.evaluateInFrame( |
| isolateId, |
| 0, |
| 'c.field', |
| idZoneId: idZone5.id, |
| ) as InstanceRef; |
| final dWithinCObjectId = dWithinCInstanceRef.id!; |
| |
| final inboundReferences = await service.getInboundReferences( |
| isolateId, |
| dWithinCObjectId, |
| 1, |
| idZoneId: idZone5.id, |
| ); |
| expect(inboundReferences.references!.length, 1); |
| expect(inboundReferences.references![0].source!.id, 'objects/8/5'); |
| |
| // Test the `idZoneId` parameter of [VmService.getRetainingPath]. |
| |
| final retainingPath = await service.getRetainingPath( |
| isolateId, |
| dWithinCObjectId, |
| 2, |
| idZoneId: idZone5.id, |
| ); |
| expect(retainingPath.length, 2); |
| expect(retainingPath.elements!.first.value!.id, 'objects/9/5'); |
| |
| // Test the `idZoneId` parameter of [VmService.getStack]. |
| |
| final stack = await service.getStack( |
| isolateId, |
| limit: 1, |
| idZoneId: idZone5.id, |
| ); |
| expect(stack.frames!.length, 1); |
| final boundVariables = stack.frames!.first.vars!; |
| expect(boundVariables.length, 1); |
| expect((boundVariables.first.value as InstanceRef).id, 'objects/11/5'); |
| |
| // Test the `idZoneId` parameter of [VmService.invoke]. |
| |
| final dLiteral2InstanceRef = await service.invoke( |
| isolateId, |
| isolate.rootLib!.id!, |
| 'getDLiteral', |
| [], |
| idZoneId: idZone5.id, |
| ) as InstanceRef; |
| expect(dLiteral2InstanceRef.id!, 'objects/12/5'); |
| }, |
| resumeIsolate, |
| ]; |
| |
| Future<void> main([args = const <String>[]]) => runIsolateTests( |
| args, |
| tests, |
| 'id_zones_test.dart', |
| testeeConcurrent: testeeMain, |
| ); |