@@ -19,6 +19,42 @@ import 'package:vm_service/vm_service.dart';
1919void main () {
2020 var timestampCounter = 0 ;
2121
22+ Event gcEvent ({
23+ required double newTime,
24+ required double oldTime,
25+ required String isolateId,
26+ }) => Event .parse ({
27+ 'kind' : EventKind .kGC,
28+ 'timestamp' : ++ timestampCounter,
29+ 'isolate' : {
30+ 'type' : '@Isolate' ,
31+ 'id' : isolateId,
32+ 'name' : 'main' ,
33+ 'number' : '1' ,
34+ },
35+ 'reason' : 'idle' ,
36+ 'new' : {
37+ 'type' : 'HeapSpace' ,
38+ 'name' : 'new' ,
39+ 'collections' : 1 ,
40+ 'avgCollectionPeriodMillis' : 100.0 ,
41+ 'used' : 1024 ,
42+ 'capacity' : 2048 ,
43+ 'external' : 0 ,
44+ 'time' : newTime,
45+ },
46+ 'old' : {
47+ 'type' : 'HeapSpace' ,
48+ 'name' : 'old' ,
49+ 'collections' : 1 ,
50+ 'avgCollectionPeriodMillis' : 100.0 ,
51+ 'used' : 4096 ,
52+ 'capacity' : 8192 ,
53+ 'external' : 0 ,
54+ 'time' : oldTime,
55+ },
56+ })! ;
57+
2258 group ('LoggingController' , () {
2359 late LoggingController controller;
2460
@@ -194,6 +230,94 @@ void main() {
194230 expect (controller.filteredData.value, isEmpty);
195231 });
196232
233+ test ('GC events track duration delta' , () async {
234+ final fakeService =
235+ serviceConnection.serviceManager.service as FakeVmServiceWrapper ;
236+
237+ expect (controller.data, isEmpty);
238+
239+ // First GC event: baseline established, no duration printed.
240+ fakeService.emitGCEvent (
241+ event: gcEvent (newTime: 0.1 , oldTime: 0.2 , isolateId: 'isolates/123' ),
242+ );
243+ await Future <void >.delayed (const Duration (milliseconds: 10 ));
244+
245+ expect (controller.data, hasLength (1 ));
246+ expect (
247+ controller.data.first.summary,
248+ 'main • idle collection • 0.0 MB used of 0.0 MB' ,
249+ );
250+
251+ // Second GC event: delta is
252+ // (0.15 + 0.25) - (0.1 + 0.2) = 0.4 - 0.3 = 0.1s = 100ms.
253+ fakeService.emitGCEvent (
254+ event: gcEvent (newTime: 0.15 , oldTime: 0.25 , isolateId: 'isolates/123' ),
255+ );
256+ await Future <void >.delayed (const Duration (milliseconds: 10 ));
257+
258+ expect (controller.data, hasLength (2 ));
259+ expect (
260+ controller.data[1 ].summary,
261+ 'main • idle collection in 100.0 ms • 0.0 MB used of 0.0 MB' ,
262+ );
263+
264+ // Third GC event on a different isolate: no baseline yet, so no duration
265+ // printed.
266+ fakeService.emitGCEvent (
267+ event: gcEvent (newTime: 0.05 , oldTime: 0.05 , isolateId: 'isolates/456' ),
268+ );
269+ await Future <void >.delayed (const Duration (milliseconds: 10 ));
270+
271+ expect (controller.data, hasLength (3 ));
272+ expect (
273+ controller.data[2 ].summary,
274+ 'main • idle collection • 0.0 MB used of 0.0 MB' ,
275+ );
276+
277+ // Fourth GC event: clock reset on isolates/123 (cumulative goes from 0.4s to 0.1s).
278+ // Since newCumulativeTime (0.1s) < previousGcTime (0.4s), duration delta is skipped,
279+ // and baseline is updated to 0.1s.
280+ fakeService.emitGCEvent (
281+ event: gcEvent (newTime: 0.05 , oldTime: 0.05 , isolateId: 'isolates/123' ),
282+ );
283+ await Future <void >.delayed (const Duration (milliseconds: 10 ));
284+
285+ expect (controller.data, hasLength (4 ));
286+ expect (
287+ controller.data[3 ].summary,
288+ 'main • idle collection • 0.0 MB used of 0.0 MB' ,
289+ );
290+
291+ // Fifth GC event: delta since reset baseline (0.1s) is (0.08 + 0.12) - 0.1 = 0.2 - 0.1 = 0.1s = 100ms.
292+ fakeService.emitGCEvent (
293+ event: gcEvent (newTime: 0.08 , oldTime: 0.12 , isolateId: 'isolates/123' ),
294+ );
295+ await Future <void >.delayed (const Duration (milliseconds: 10 ));
296+
297+ expect (controller.data, hasLength (5 ));
298+ expect (
299+ controller.data[4 ].summary,
300+ 'main • idle collection in 100.0 ms • 0.0 MB used of 0.0 MB' ,
301+ );
302+
303+ // Clear logs: resets baselines.
304+ controller.clear ();
305+ expect (controller.data, isEmpty);
306+
307+ // Sixth GC event (same isolate as first): baseline was cleared, so no
308+ // duration printed.
309+ fakeService.emitGCEvent (
310+ event: gcEvent (newTime: 0.2 , oldTime: 0.3 , isolateId: 'isolates/123' ),
311+ );
312+ await Future <void >.delayed (const Duration (milliseconds: 10 ));
313+
314+ expect (controller.data, hasLength (1 ));
315+ expect (
316+ controller.data.first.summary,
317+ 'main • idle collection • 0.0 MB used of 0.0 MB' ,
318+ );
319+ });
320+
197321 test ('matchesForSearch - default filters' , () {
198322 prepareTestLogs ();
199323
0 commit comments