blob: df22cc3b78b10e9f6b5f21240f2f532090ed7397 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. 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:devtools_shared/devtools_test_utils.dart';
import 'package:flutter_test/flutter_test.dart';
import 'integration.dart';
void debuggingTests() {
late CliAppFixture appFixture;
late BrowserTabInstance tabInstance;
setUp(() async {
tabInstance = await browserManager.createNewTab();
});
tearDown(() async {
await tabInstance.close();
await appFixture.teardown();
});
test('lists scripts', () async {
appFixture = await CliAppFixture.create('test/fixtures/debugging_app.dart');
final DevtoolsManager tools =
DevtoolsManager(tabInstance, webBuildFixture.baseUri);
await tools.start(appFixture);
await tools.switchPage('debugger');
final String? currentPageId = await tools.currentPageId();
expect(currentPageId, 'debugger');
final DebuggingManager debuggingManager = DebuggingManager(tools);
// Allow some time for the scripts view to be populated, as it requires
// some isolate events to fire that we have not already waited for.
await waitFor(
() async => (await debuggingManager.getScripts()).isNotEmpty,
timeoutMessage: 'Scripts view was not populated',
);
final List<String> scripts = await debuggingManager.getScripts();
expect(scripts, isNotEmpty);
expect(scripts, anyElement(endsWith(appFixture.appScriptPath)));
});
test('breakpoints, variables, resume', () async {
appFixture = await CliAppFixture.create('test/fixtures/debugging_app.dart');
final String source = appFixture.scriptSource;
final List<int> breakpointLines =
CliAppFixture.parseBreakpointLines(source);
final DevtoolsManager tools =
DevtoolsManager(tabInstance, webBuildFixture.baseUri);
await tools.start(appFixture);
await tools.switchPage('debugger');
final String? currentPageId = await tools.currentPageId();
expect(currentPageId, 'debugger');
final DebuggingManager debuggingManager = DebuggingManager(tools);
// clear and verify breakpoints
List<String> breakpoints = await debuggingManager.getBreakpoints();
expect(breakpoints, isEmpty);
await delay();
// set and verify breakpoints
for (int line in breakpointLines) {
await debuggingManager.addBreakpoint(appFixture.appScriptPath, line);
}
breakpoints = await debuggingManager.getBreakpoints();
expect(breakpoints, isNotEmpty);
// wait for paused state
await waitFor(() async => await debuggingManager.getState() == 'paused');
await delay();
// verify location
expect(
await debuggingManager.getLocation(),
endsWith('${appFixture.appScriptPath}:${breakpointLines.first}'),
);
// verify stack frame
final List<String> frames = await debuggingManager.getCallStackFrames();
expect(frames.length, greaterThan(2));
expect(frames.sublist(0, 2), [
'Cat.performAction:debugging_app.dart',
'main.run.<anonymous closure>:debugging_app.dart',
]);
// verify variables
expect(
await debuggingManager.getVariables(),
unorderedEquals(['this:Cat', 'actionStr:catAction!']),
);
// resume
await debuggingManager.clearBreakpoints();
await debuggingManager.resume();
await delay();
// verify state resumed
expect(await debuggingManager.getState(), 'running');
});
test('stepping, async step, async frames', () async {
appFixture =
await CliAppFixture.create('test/fixtures/debugging_app_async.dart');
final String source = appFixture.scriptSource;
final int breakpointLine =
CliAppFixture.parseBreakpointLines(source).single;
final List<int> steppingLines = CliAppFixture.parseSteppingLines(source);
final DevtoolsManager tools =
DevtoolsManager(tabInstance, webBuildFixture.baseUri);
await tools.start(appFixture);
await tools.switchPage('debugger');
final String? currentPageId = await tools.currentPageId();
expect(currentPageId, 'debugger');
final DebuggingManager debuggingManager = DebuggingManager(tools);
// clear and verify breakpoints
List<String> breakpoints = await debuggingManager.getBreakpoints();
expect(breakpoints, isEmpty);
await delay();
// set and verify breakpoint
await debuggingManager.addBreakpoint(
appFixture.appScriptPath,
breakpointLine,
);
breakpoints = await debuggingManager.getBreakpoints();
expect(breakpoints, hasLength(1));
// wait for paused state
await waitFor(() async => await debuggingManager.getState() == 'paused');
await shortDelay();
// verify location
expect(
await debuggingManager.getLocation(),
endsWith('${appFixture.appScriptPath}:$breakpointLine'),
);
// test stepping
for (int stepLine in steppingLines) {
// step
await debuggingManager.step();
// wait for paused state
await waitFor(() async => await debuggingManager.getState() == 'paused');
await delay();
// verify location
expect(
await debuggingManager.getLocation(),
endsWith('${appFixture.appScriptPath}:$stepLine'),
);
}
// verify an async stack frame
final List<String> frames = await debuggingManager.getCallStackFrames();
expect(frames.length, greaterThan(4));
expect(frames.sublist(0, 4), [
'performAction:debugging_app_async.dart',
'<async break>',
'main.run.<anonymous closure>:debugging_app_async.dart',
'<async break>',
]);
// resume
await debuggingManager.clearBreakpoints();
await debuggingManager.resume();
await delay();
// verify state resumed
expect(await debuggingManager.getState(), 'running');
});
test('break on exceptions', () async {
appFixture = await CliAppFixture.create(
'test/fixtures/debugging_app_exception.dart',
);
final String source = appFixture.scriptSource;
final int exceptionLine = CliAppFixture.parseExceptionLines(source).first;
final DevtoolsManager tools =
DevtoolsManager(tabInstance, webBuildFixture.baseUri);
await tools.start(appFixture);
await tools.switchPage('debugger');
final String? currentPageId = await tools.currentPageId();
expect(currentPageId, 'debugger');
final DebuggingManager debuggingManager = DebuggingManager(tools);
// verify running state
expect(await debuggingManager.getState(), 'running');
// set break on exceptions mode
await debuggingManager.setIsolatePauseMode('All');
// wait for paused state
await waitFor(() async => await debuggingManager.getState() == 'paused');
await delay();
// verify location
expect(
await debuggingManager.getLocation(),
endsWith('${appFixture.appScriptPath}:$exceptionLine'),
);
// verify locals, including the exception object
expect(await debuggingManager.getVariables(), [
'<exception>:StateError',
'foo:2',
]);
// resume
await debuggingManager.setIsolatePauseMode('Unhandled');
await debuggingManager.resume();
await delay();
// verify state resumed
expect(await debuggingManager.getState(), 'running');
});
test('console output', () async {
appFixture = await CliAppFixture.create(
'test/fixtures/color_console_output_app.dart',
);
final tools = DevtoolsManager(tabInstance, webBuildFixture.baseUri);
await tools.start(appFixture);
await tools.switchPage('debugger');
final currentPageId = await tools.currentPageId();
expect(currentPageId, 'debugger');
final debuggingManager = DebuggingManager(tools);
// This test app must start paused so we don't have race conditions where
// we miss some console output that was emitted too early.
await waitFor(() async => await debuggingManager.getState() == 'paused');
await debuggingManager.resume();
expect(await debuggingManager.getState(), 'running');
// Wait until there is enough console output.
await waitFor(
() async =>
(await debuggingManager.getConsoleContents())!.split('\n').length >=
13,
);
// Verify the console contents.
expect(
await debuggingManager.getConsoleContents(),
startsWith(
'starting ansi color app\n'
'<span style=\'background-color: rgb(0,0,0);color: rgb(255,255,255)\'>0 <span style=\'color: rgb(0,0,0)\'> </span>0 <span style=\'background-color: rgb(187,0,0);color: rgb(255,255,255)\'>1</span> <span style=\'color: rgb(187,0,0)\'> </span>1 <span style=\'background-color: rgb(0,187,0);color: rgb(255,255,255)\'>2</span> <span style=\'color: rgb(0,187,0)\'> </span>2 <span style=\'background-color: rgb(187,187,0);color: rgb(255,255,255)\'>3</span> <span style=\'color: rgb(187,187,0)\'> </span>3 <span style=\'background-color: rgb(0,0,187);color: rgb(255,255,255)\'>4</span> <span style=\'color: rgb(0,0,187)\'> </span>4 <span style=\'background-color: rgb(187,0,187);color: rgb(255,255,255)\'>5</span> <span style=\'color: rgb(187,0,187)\'> </span>5 <span style=\'background-color: rgb(0,187,187);color: rgb(255,255,255)\'>6</span> <span style=\'color: rgb(0,187,187)\'> </span>6 <span style=\'background-color: rgb(255,255,255);color: rgb(255,255,255)\'>7</span> <span style=\'color: rgb(255,255,255)\'> </span>7 \n'
'<span style=\'background-color: rgb(85,85,85);color: rgb(255,255,255)\'>8 <span style=\'color: rgb(85,85,85)\'> </span>8 <span style=\'background-color: rgb(255,85,85);color: rgb(255,255,255)\'>9</span> <span style=\'color: rgb(255,85,85)\'> </span>9 <span style=\'background-color: rgb(0,255,0);color: rgb(255,255,255)\'>1</span>0 <span style=\'color: rgb(0,255,0)\'> </span>10 <span style=\'background-color: rgb(255,255,85);color: rgb(255,255,255)\'>1</span>1 <span style=\'color: rgb(255,255,85)\'> </span>11 <span style=\'background-color: rgb(85,85,255);color: rgb(255,255,255)\'>1</span>2 <span style=\'color: rgb(85,85,255)\'> </span>12 <span style=\'background-color: rgb(255,85,255);color: rgb(255,255,255)\'>1</span>3 <span style=\'color: rgb(255,85,255)\'> </span>13 <span style=\'background-color: rgb(85,255,255);color: rgb(255,255,255)\'>1</span>4 <span style=\'color: rgb(85,255,255)\'> </span>14 <span style=\'background-color: rgb(255,255,255);color: rgb(255,255,255)\'>1</span>5 <span style=\'color: rgb(255,255,255)\'> </span>15 \n'
'\n'
'<span style=\'background-color: rgb(0,0,0);color: rgb(255,255,255)\'> 16 <span style=\'color: rgb(0,0,0)\'> </span>16 <span style=\'background-color: rgb(0,0,175);color: rgb(255,255,255)\'> </span>19 <span style=\'color: rgb(0,0,175)\'> </span>19 \n'
'<span style=\'background-color: rgb(0,175,0);color: rgb(255,255,255)\'> 34 <span style=\'color: rgb(0,175,0)\'> </span>34 <span style=\'background-color: rgb(0,175,175);color: rgb(255,255,255)\'> </span>37 <span style=\'color: rgb(0,175,175)\'> </span>37 \n'
'\n'
'<span style=\'background-color: rgb(175,0,0);color: rgb(255,255,255)\'> 124 <span style=\'color: rgb(175,0,0)\'> </span>124 <span style=\'background-color: rgb(175,0,175);color: rgb(255,255,255)\'> </span>127 <span style=\'color: rgb(175,0,175)\'> </span>127 \n'
'<span style=\'background-color: rgb(175,175,0);color: rgb(255,255,255)\'> 142 <span style=\'color: rgb(175,175,0)\'> </span>142 <span style=\'background-color: rgb(175,175,175);color: rgb(255,255,255)\'> </span>145 <span style=\'color: rgb(175,175,175)\'> </span>145 \n'
'\n'
'<span style=\'background-color: rgb(8,8,8);color: rgb(255,255,255)\'> 232 <span style=\'color: rgb(8,8,8)\'> </span>232 <span style=\'background-color: rgb(18,18,18);color: rgb(255,255,255)\'> </span>233 <span style=\'color: rgb(18,18,18)\'> </span>233 <span style=\'background-color: rgb(28,28,28);color: rgb(255,255,255)\'> </span>234 <span style=\'color: rgb(28,28,28)\'> </span>234 <span style=\'background-color: rgb(38,38,38);color: rgb(255,255,255)\'> </span>235 <span style=\'color: rgb(38,38,38)\'> </span>235 <span style=\'background-color: rgb(48,48,48);color: rgb(255,255,255)\'> </span>236 <span style=\'color: rgb(48,48,48)\'> </span>236 <span style=\'background-color: rgb(58,58,58);color: rgb(255,255,255)\'> </span>237 <span style=\'color: rgb(58,58,58)\'> </span>237 <span style=\'background-color: rgb(68,68,68);color: rgb(255,255,255)\'> </span>238 <span style=\'color: rgb(68,68,68)\'> </span>238 <span style=\'background-color: rgb(78,78,78);color: rgb(255,255,255)\'> </span>239 <span style=\'color: rgb(78,78,78)\'> </span>239 \n'
'<span style=\'background-color: rgb(88,88,88);color: rgb(255,255,255)\'> 240 <span style=\'color: rgb(88,88,88)\'> </span>240 <span style=\'background-color: rgb(98,98,98);color: rgb(255,255,255)\'> </span>241 <span style=\'color: rgb(98,98,98)\'> </span>241 <span style=\'background-color: rgb(108,108,108);color: rgb(255,255,255)\'> </span>242 <span style=\'color: rgb(108,108,108)\'> </span>242 <span style=\'background-color: rgb(118,118,118);color: rgb(255,255,255)\'> </span>243 <span style=\'color: rgb(118,118,118)\'> </span>243 <span style=\'background-color: rgb(128,128,128);color: rgb(255,255,255)\'> </span>244 <span style=\'color: rgb(128,128,128)\'> </span>244 <span style=\'background-color: rgb(138,138,138);color: rgb(255,255,255)\'> </span>245 <span style=\'color: rgb(138,138,138)\'> </span>245 <span style=\'background-color: rgb(148,148,148);color: rgb(255,255,255)\'> </span>246 <span style=\'color: rgb(148,148,148)\'> </span>246 <span style=\'background-color: rgb(158,158,158);color: rgb(255,255,255)\'> </span>247 <span style=\'color: rgb(158,158,158)\'> </span>247 \n'
'<span style=\'background-color: rgb(168,168,168);color: rgb(255,255,255)\'> 248 <span style=\'color: rgb(168,168,168)\'> </span>248 <span style=\'background-color: rgb(178,178,178);color: rgb(255,255,255)\'> </span>249 <span style=\'color: rgb(178,178,178)\'> </span>249 <span style=\'background-color: rgb(188,188,188);color: rgb(255,255,255)\'> </span>250 <span style=\'color: rgb(188,188,188)\'> </span>250 <span style=\'background-color: rgb(198,198,198);color: rgb(255,255,255)\'> </span>251 <span style=\'color: rgb(198,198,198)\'> </span>251 <span style=\'background-color: rgb(208,208,208);color: rgb(255,255,255)\'> </span>252 <span style=\'color: rgb(208,208,208)\'> </span>252 <span style=\'background-color: rgb(218,218,218);color: rgb(255,255,255)\'> </span>253 <span style=\'color: rgb(218,218,218)\'> </span>253 <span style=\'background-color: rgb(228,228,228);color: rgb(255,255,255)\'> </span>254 <span style=\'color: rgb(228,228,228)\'> </span>254 <span style=\'background-color: rgb(238,238,238);color: rgb(255,255,255)\'> </span>255 <span style=\'color: rgb(238,238,238)\'> </span>255 \n',
),
);
});
test('pause', () async {
appFixture = await CliAppFixture.create('test/fixtures/debugging_app.dart');
final DevtoolsManager tools =
DevtoolsManager(tabInstance, webBuildFixture.baseUri);
await tools.start(appFixture);
await tools.switchPage('debugger');
final String? currentPageId = await tools.currentPageId();
expect(currentPageId, 'debugger');
final DebuggingManager debuggingManager = DebuggingManager(tools);
// verify running state
expect(await debuggingManager.getState(), 'running');
// pause
await debuggingManager.pause();
// wait for paused state
await waitFor(() async => await debuggingManager.getState() == 'paused');
expect(await debuggingManager.getState(), 'paused');
// resume
await debuggingManager.resume();
await delay();
// verify state resumed
expect(await debuggingManager.getState(), 'running');
});
}
class DebuggingManager {
DebuggingManager(this.tools);
final DevtoolsManager tools;
Future<void> resume() async {
await tools.tabInstance.send('debugger.resume');
}
Future<void> pause() async {
await tools.tabInstance.send('debugger.pause');
}
Future<void> step() async {
await tools.tabInstance.send('debugger.step');
}
Future<String?> getLocation() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getLocation');
return response.result as String?;
}
Future<List<String>> getVariables() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getVariables');
final List<dynamic> result = response.result as List;
return result.cast<String>();
}
Future<String?> getState() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getState');
return response.result as String?;
}
Future<String?> getConsoleContents() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getConsoleContents');
return response.result as String?;
}
Future<void> clearBreakpoints() async {
await tools.tabInstance.send('debugger.clearBreakpoints');
}
Future<void> addBreakpoint(String path, int line) async {
await tools.tabInstance.send('debugger.addBreakpoint', [path, line]);
}
Future<void> setIsolatePauseMode(String mode) async {
await tools.tabInstance.send('debugger.setIsolatePauseMode', mode);
}
Future<List<String>> getBreakpoints() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getBreakpoints');
final List<dynamic> result = response.result as List;
return result.cast<String>();
}
Future<List<String>> getScripts() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getScripts');
final List<dynamic> result = response.result as List;
return result.cast<String>();
}
Future<bool> supportsScripts() async {
final AppResponse response =
await tools.tabInstance.send('debugger.supportsScripts');
return response.result as bool;
}
Future<List<String>> getCallStackFrames() async {
final AppResponse response =
await tools.tabInstance.send('debugger.getCallStackFrames');
final List<dynamic> result = response.result as List;
return result.cast<String>();
}
}