// Copyright (c) 2020, 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.

// VMOptions=--verbose_debug

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/get_stack_test.dart
//
const LINE_0 = 41;
const LINE_A = 43;
const LINE_1 = 44;
const LINE_B = 45;
// AUTOGENERATED END

Future<void> testMain() async {
  await func1();
}

Future func1() async => await func2();
Future func2() async => await func3();
Future func3() async => await func4();
Future func4() async => await func5();
Future func5() async => await func6();
Future func6() async => await func7();
Future func7() async => await func8();
Future func8() async => await func9();
Future func9() async => await func10();
Future func10() async {
  debugger(); // LINE_0
  // ignore: await_only_futures
  await 0; // LINE_A
  debugger(); // LINE_1
  print('Hello, world!'); // LINE_B
}

void expectFrame(
  final frame,
  final kindExpectation,
  final codeNameExpectation,
) {
  expect(frame.kind, kindExpectation);
  expect(frame.code?.name, codeNameExpectation);
}

void expectFrames(final frames, final expectKindAndCodeName) {
  for (int i = 0; i < expectKindAndCodeName.length; i++) {
    expectFrame(
      frames[i],
      expectKindAndCodeName[i][0],
      expectKindAndCodeName[i][1],
    );
  }
}

final tests = <IsolateTest>[
  // Before the first await.
  hasStoppedAtBreakpoint,
  stoppedAtLine(LINE_0),
  stepOver,
  hasStoppedAtBreakpoint,
  stoppedAtLine(LINE_A),
  // At LINE_A we're still running sync. so no asyncCausalFrames.
  (VmService service, IsolateRef isolateRef) async {
    final result = await service.getStack(isolateRef.id!);

    expect(result.frames, hasLength(16));
    expect(result.asyncCausalFrames, isNull);

    expectFrames(result.frames, [
      [equals('Regular'), endsWith(' func10')],
      [equals('Regular'), endsWith(' func9')],
      [equals('Regular'), endsWith(' func8')],
      [equals('Regular'), endsWith(' func7')],
      [equals('Regular'), endsWith(' func6')],
      [equals('Regular'), endsWith(' func5')],
      [equals('Regular'), endsWith(' func4')],
      [equals('Regular'), endsWith(' func3')],
      [equals('Regular'), endsWith(' func2')],
      [equals('Regular'), endsWith(' func1')],
      [equals('Regular'), endsWith(' testMain')],
    ]);
  },
  resumeIsolate,
  hasStoppedAtBreakpoint,
  stoppedAtLine(LINE_1),
  stepOver,
  hasStoppedAtBreakpoint,
  stoppedAtLine(LINE_B),
  // After resuming the continuation - i.e. running async.
  (VmService service, IsolateRef isolateRef) async {
    final result = await service.getStack(isolateRef.id!);

    expect(result.frames, hasLength(5));
    expect(result.asyncCausalFrames, hasLength(26));

    expectFrames(result.frames!, [
      [equals('Regular'), endsWith(' func10')],
      [equals('Regular'), anything], // Internal mech. ..
      [equals('Regular'), anything],
      [equals('Regular'), anything],
      [equals('Regular'), endsWith(' _RawReceivePort._handleMessage')],
    ]);

    expectFrames(result.asyncCausalFrames, [
      [equals('Regular'), endsWith(' func10')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func9')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func8')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func7')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func6')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func5')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func4')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func3')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func2')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' func1')],
      [equals('AsyncSuspensionMarker'), isNull],
      [equals('AsyncCausal'), endsWith(' testMain')],
      [equals('AsyncSuspensionMarker'), isNull],
    ]);
  },
];

void main([args = const <String>[]]) => runIsolateTests(
      args,
      tests,
      'get_stack_test.dart',
      testeeConcurrent: testMain,
    );
