| // Copyright (c) 2023, 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. |
| |
| // ignore_for_file: empty_catches |
| |
| 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 <test.dart> |
| // |
| const LINE_A = 33; |
| const LINE_B = 47; |
| const LINE_C = 59; |
| const LINE_D = 74; |
| const LINE_E = 92; |
| const LINE_F = 108; |
| const LINE_G = 122; |
| // AUTOGENERATED END |
| |
| // break statement |
| Stream<int> testBreak() async* { |
| for (int t = 0; t < 10; t++) { |
| try { |
| if (t == 1) break; |
| await throwException(); // LINE_A |
| } catch (e) { |
| } finally { |
| yield t; |
| } |
| } |
| } |
| |
| // return statement |
| Stream<int> testReturn() async* { |
| for (int t = 0; t < 10; t++) { |
| try { |
| yield t; |
| if (t == 1) return; |
| await throwException(); // LINE_B |
| } catch (e) { |
| } finally { |
| yield t; |
| } |
| } |
| } |
| |
| // Multiple functions |
| Stream<int> testMultipleFunctions() async* { |
| try { |
| yield 0; |
| await throwException(); // LINE_C |
| } catch (e) { |
| } finally { |
| yield 1; |
| } |
| } |
| |
| // continue statement |
| Stream<int> testContinueSwitch() async* { |
| final int currentState = 0; |
| switch (currentState) { |
| case 0: |
| { |
| try { |
| if (currentState == 1) continue label; |
| await throwException(); // LINE_D |
| } catch (e) { |
| } finally { |
| yield 0; |
| } |
| yield 1; |
| break; |
| } |
| label: |
| case 1: |
| break; |
| } |
| } |
| |
| Stream<int> testNestFinally() async* { |
| final int i = 0; |
| try { |
| if (i == 1) return; |
| await throwException(); // LINE_E |
| } catch (e) { |
| } finally { |
| try { |
| yield i; |
| } finally { |
| yield 1; |
| } |
| yield 1; |
| } |
| } |
| |
| Stream<int> testAsyncClosureInFinally() async* { |
| final int i = 0; |
| try { |
| if (i == 1) return; |
| await throwException(); // LINE_F |
| } catch (e) { |
| } finally { |
| Future<void> inner() async { |
| await Future.delayed(Duration(milliseconds: 10)); |
| } |
| |
| await inner(); |
| yield 1; |
| } |
| } |
| |
| Future<void> throwException() async { |
| await Future.delayed(Duration(milliseconds: 10)); |
| throw Exception(''); // LINE_G |
| } |
| |
| Future<void> code() async { |
| await for (var _ in testBreak()) {} |
| await for (var _ in testReturn()) {} |
| await for (var _ in testMultipleFunctions()) {} |
| await for (var _ in testContinueSwitch()) {} |
| await for (var _ in testNestFinally()) {} |
| await for (var _ in testAsyncClosureInFinally()) {} |
| } |
| |
| final tests = <IsolateTest>[ |
| hasPausedAtStart, |
| setBreakpointAtLine(LINE_G), |
| resumeIsolate, |
| for (final line in [LINE_A, LINE_B, LINE_C, LINE_D, LINE_E, LINE_F]) ...[ |
| hasStoppedAtBreakpoint, |
| stoppedAtLine(LINE_G), |
| _expectSecondFrameFromTheTopToBeAt(line), |
| resumeIsolate, |
| ], |
| hasStoppedAtExit, |
| ]; |
| |
| Future<void> Function(VmService, IsolateRef) _expectSecondFrameFromTheTopToBeAt( |
| int line, |
| ) { |
| return (VmService service, IsolateRef isolateRef) async { |
| final isolateId = isolateRef.id!; |
| final stack = await service.getStack(isolateId); |
| final frames = stack.asyncCausalFrames!; |
| expect(frames.length, greaterThanOrEqualTo(3)); |
| |
| // Check second top frame contains correct line number. |
| expect(frames[0].kind, FrameKind.kRegular); |
| final frame0Location = frames[0].location!; |
| final script0 = await service.getObject( |
| isolateId, |
| frame0Location.script!.id!, |
| ) as Script; |
| expect( |
| script0.getLineNumberFromTokenPos(frame0Location.tokenPos!), |
| LINE_G, |
| ); |
| expect(frames[1].kind, FrameKind.kAsyncSuspensionMarker); |
| expect(frames[2].location, isNotNull); |
| expect(frames[2].kind, FrameKind.kAsyncCausal); |
| final frame2Location = frames[2].location!; |
| final script2 = await service.getObject( |
| isolateId, |
| frame2Location.script!.id!, |
| ) as Script; |
| expect( |
| script2.getLineNumberFromTokenPos(frames[2].location!.tokenPos!), |
| line, |
| ); |
| }; |
| } |
| |
| void main([args = const <String>[]]) => runIsolateTests( |
| args, |
| tests, |
| 'yield_positions_with_finally_test.dart', |
| testeeConcurrent: code, |
| pauseOnStart: true, |
| pauseOnExit: true, |
| ); |