// 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 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; import 'common/service_test_common.dart'; import 'id_zones_lib.dart' as testee_lib; void main([args = const []]) => IsolateTestHarness( 'id_zones_lib.dart', args, ) .hasStoppedAtBreakpoint() .stoppedAtLine('LINE_A') // Test the behaviour of an ID Zone with a `backingBufferKind` of `Ring`, an // `idAssignmentPolicy` of `AlwaysAllocate`, and the default capacity. .addCustomTest((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'); await service.callMethod('_collectAllGarbage', isolateId: isolateId); 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('')); } // 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. .addCustomTest((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('')); } }) // Test the behaviour of an ID Zone with a `backingBufferKind` of `Ring`, an // `idAssignmentPolicy` of `ReuseExisting`, and the default capacity. .addCustomTest((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!, ); await service.callMethod('_collectAllGarbage', isolateId: isolateId); 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('')); } // 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. .addCustomTest((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!, ); await service.callMethod('_collectAllGarbage', isolateId: isolateId); 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. .addCustomTest((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.libraries! .firstWhere((l) => l.uri!.contains('id_zones_lib')) .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.libraries! .firstWhere((l) => l.uri!.contains('id_zones_lib')) .id!, 'getDLiteral', [], idZoneId: idZone5.id, ) as InstanceRef; expect(dLiteral2InstanceRef.id!, 'objects/12/5'); }) .resumeIsolate() .run(testeeMain: testee_lib.main);