diff --git a/packages/devtools_app/lib/src/screens/logging/_message_column.dart b/packages/devtools_app/lib/src/screens/logging/_message_column.dart index 8e384813c6d..371b8856677 100644 --- a/packages/devtools_app/lib/src/screens/logging/_message_column.dart +++ b/packages/devtools_app/lib/src/screens/logging/_message_column.dart @@ -92,10 +92,7 @@ class MessageColumn extends ColumnData padding: const EdgeInsets.symmetric(vertical: densePadding), child: LayoutBuilder( builder: (context, constraints) { - return MetadataChips( - data: data, - maxWidth: constraints.maxWidth, - ); + return MetadataChips(data: data); }, ), ), diff --git a/packages/devtools_app/lib/src/screens/logging/metadata.dart b/packages/devtools_app/lib/src/screens/logging/metadata.dart index 7860ad818e2..40a8d851560 100644 --- a/packages/devtools_app/lib/src/screens/logging/metadata.dart +++ b/packages/devtools_app/lib/src/screens/logging/metadata.dart @@ -13,16 +13,9 @@ import '../../shared/primitives/utils.dart'; import 'logging_controller.dart'; class MetadataChips extends StatelessWidget { - const MetadataChips({ - super.key, - required this.data, - // TODO(kenz): remove maxWidth from these metadata chips once the Logging - // V2 code is removed. - required this.maxWidth, - }); + const MetadataChips({super.key, required this.data}); final LogData data; - final double maxWidth; @override Widget build(BuildContext context) { @@ -41,7 +34,6 @@ class MetadataChips extends StatelessWidget { final logLevelChip = LogLevelMetadataChip( level: logLevel, rawLevel: data.level, - maxWidth: maxWidth, backgroundColor: logLevelColors.background, foregroundColor: logLevelColors.foreground, ); @@ -53,7 +45,6 @@ class MetadataChips extends StatelessWidget { isolateChip = IsolateChip( name: isolateName, id: data.isolateRef?.id, - maxWidth: maxWidth, backgroundColor: colorScheme.surface, foregroundColor: colorScheme.onSurface, outlined: true, @@ -68,7 +59,6 @@ class MetadataChips extends StatelessWidget { zoneChip = ZoneChip( name: zoneName, identityHashCode: zone!.identityHashCode, - maxWidth: maxWidth, backgroundColor: colorScheme.surface, foregroundColor: colorScheme.onSurface, outlined: true, @@ -96,7 +86,6 @@ class MetadataChips extends StatelessWidget { children: [ KindMetaDataChip( kind: data.kind, - maxWidth: maxWidth, icon: kindIcon.icon, iconAsset: kindIcon.iconAsset, backgroundColor: kindColors.background, @@ -105,7 +94,6 @@ class MetadataChips extends StatelessWidget { logLevelChip, if (elapsedFrameTimeAsString != null) FrameElapsedMetaDataChip( - maxWidth: maxWidth, elapsedTimeDisplay: elapsedFrameTimeAsString, ), if (isolateChip != null) isolateChip, @@ -118,7 +106,6 @@ class MetadataChips extends StatelessWidget { abstract class MetadataChip extends StatelessWidget { const MetadataChip({ super.key, - required this.maxWidth, required this.text, this.tooltip, this.icon, @@ -129,7 +116,6 @@ abstract class MetadataChip extends StatelessWidget { this.includeLeadingMargin = true, }); - final double maxWidth; final IconData? icon; final String? iconAsset; final String text; @@ -156,7 +142,6 @@ abstract class MetadataChip extends StatelessWidget { return maybeWrapWithTooltip( tooltip: tooltip, child: Container( - constraints: BoxConstraints(maxWidth: maxWidth), decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(_borderRadius), @@ -207,7 +192,6 @@ class KindMetaDataChip extends MetadataChip { const KindMetaDataChip({ super.key, required String kind, - required super.maxWidth, super.icon, super.iconAsset, super.backgroundColor, @@ -251,7 +235,6 @@ class KindMetaDataChip extends MetadataChip { class FrameElapsedMetaDataChip extends MetadataChip { const FrameElapsedMetaDataChip({ super.key, - required super.maxWidth, required String elapsedTimeDisplay, }) : super(icon: Icons.timer, text: elapsedTimeDisplay); } @@ -261,7 +244,6 @@ class LogLevelMetadataChip extends MetadataChip { super.key, required Level level, required int rawLevel, - required super.maxWidth, super.backgroundColor, super.foregroundColor, }) : super(text: 'Level.${level.name} ($rawLevel)'); @@ -307,7 +289,6 @@ class IsolateChip extends MetadataChip { super.key, required String name, required String? id, - required super.maxWidth, super.backgroundColor, super.foregroundColor, super.outlined = false, @@ -319,7 +300,6 @@ class ZoneChip extends MetadataChip { super.key, required String name, required int? identityHashCode, - required super.maxWidth, super.backgroundColor, super.foregroundColor, super.outlined = false, diff --git a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart index 1bf9602c29c..1a4bba40f7b 100644 --- a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart +++ b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart @@ -456,14 +456,8 @@ class HttpRequestCookiesView extends StatelessWidget { label: Expanded( child: Text( title, - // TODO(kenz): use top level overflow parameter if - // https://github.com/flutter/flutter/issues/82722 is fixed. - // TODO(kenz): add overflow after flutter 2.3.0 is stable. It was - // added in commit 65388ee2eeaf0d2cf087eaa4a325e3689020c46a. - // style: theme.textTheme.titleMedium.copyWith( - // overflow: TextOverflow.fade, - // ), style: theme.textTheme.titleMedium, + overflow: TextOverflow.ellipsis, ), ), numeric: numeric, diff --git a/packages/devtools_app/lib/src/screens/network/network_screen.dart b/packages/devtools_app/lib/src/screens/network/network_screen.dart index ce7e247af21..7014600594d 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -295,13 +295,11 @@ class NetworkRequestsTable extends StatelessWidget { Widget build(BuildContext context) { return RoundedOutlinedBorder( clip: true, - // TODO(kenz): use SearchableFlatTable instead. - child: FlatTable( - keyFactory: (NetworkRequest? data) => ValueKey(data), + child: SearchableFlatTable( + searchController: networkController, + keyFactory: (NetworkRequest data) => ValueKey(data), data: requests, dataKey: 'network-requests', - searchMatchesNotifier: searchMatchesNotifier, - activeSearchMatchNotifier: activeSearchMatchNotifier, autoScrollContent: true, columns: columns, selectionNotifier: networkController.selectedRequest, diff --git a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart index b97cd7da2f4..da8b2b4de23 100644 --- a/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart +++ b/packages/devtools_app/lib/src/screens/performance/panes/timeline_events/timeline_events_controller.dart @@ -282,9 +282,7 @@ class TimelineEventsController extends PerformanceFeatureController // iOS: "io.flutter.1.raster (12651)" // Linux, Windows, Dream (g3): "io.flutter.raster (12651)" // MacOS: Does not exist - // Also look for .gpu here for older versions of Flutter. - // TODO(kenz): remove check for .gpu name in April 2021. - if (name.contains(rasterThreadSuffix) || name.contains(gpuThreadSuffix)) { + if (name.contains(rasterThreadSuffix)) { rasterTrackId = id; } diff --git a/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart b/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart index 41fdc2dcb45..809eaee23e0 100644 --- a/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart +++ b/packages/devtools_app/lib/src/screens/profiler/panes/controls/profiler_screen_controls.dart @@ -32,24 +32,22 @@ class ProfilerScreenControls extends StatelessWidget { @override Widget build(BuildContext context) { - // TODO(kenz): use the [OfflineAwareControls] helper widget. - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (offline) - Padding( - padding: const EdgeInsets.only(right: defaultSpacing), - child: ExitOfflineButton(gaScreen: gac.cpuProfiler), - ) - else ...[ - _PrimaryControls(controller: controller, recording: recording), - const SizedBox(width: defaultSpacing), - _SecondaryControls( - controller: controller, - profilerBusy: recording || processing, - ), - ], - ], + return OfflineAwareControls( + gaScreen: gac.cpuProfiler, + controlsBuilder: (offline) { + if (offline) return const SizedBox.shrink(); + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _PrimaryControls(controller: controller, recording: recording), + const SizedBox(width: defaultSpacing), + _SecondaryControls( + controller: controller, + profilerBusy: recording || processing, + ), + ], + ); + }, ); } } diff --git a/packages/devtools_app/lib/src/shared/framework/screen.dart b/packages/devtools_app/lib/src/shared/framework/screen.dart index e664a004f66..08aaebd9378 100644 --- a/packages/devtools_app/lib/src/shared/framework/screen.dart +++ b/packages/devtools_app/lib/src/shared/framework/screen.dart @@ -19,7 +19,6 @@ import '../ui/icons.dart'; final _log = Logger('screen.dart'); -// TODO(kenz): use correct assets. enum ScreenMetaData { home( 'home', diff --git a/packages/devtools_app/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/devtools_app/macos/Flutter/GeneratedPluginRegistrant.swift index b4599a264a3..4e44bc88d93 100644 --- a/packages/devtools_app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/packages/devtools_app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -1,6 +1,3 @@ -// Copyright 2025 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. // // Generated file. Do not edit. // diff --git a/packages/devtools_app/test/legacy_integration_tests/app.dart b/packages/devtools_app/test/legacy_integration_tests/app.dart deleted file mode 100644 index cb14aa6a13f..00000000000 --- a/packages/devtools_app/test/legacy_integration_tests/app.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. - -import 'package:devtools_shared/devtools_test_utils.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'integration.dart'; - -void appTests() { - late CliAppFixture appFixture; - late BrowserTabInstance tabInstance; - - setUp(() async { - appFixture = await CliAppFixture.create( - 'test/test_infra/fixtures/logging_app.dart', - ); - tabInstance = await browserManager.createNewTab(); - }); - - tearDown(() async { - await tabInstance.close(); - await appFixture.teardown(); - }); - - test('can switch pages', () async { - final tools = DevtoolsManager(tabInstance, webBuildFixture.baseUri); - await tools.start(appFixture); - await tools.switchPage('logging'); - - final currentPageId = await tools.currentPageId(); - expect(currentPageId, 'logging'); - }); - - test('connect dialog displays', () async { - // start with no port - final baseAppUri = webBuildFixture.baseUri.resolve('index.html'); - final tools = DevtoolsManager(tabInstance, webBuildFixture.baseUri); - await tools.start( - appFixture, - overrideUri: baseAppUri, - waitForConnection: false, - ); - - final connectDialog = ConnectDialogManager(tools); - - // make sure the connect dialog displays - await waitFor(() async => await connectDialog.isVisible()); - - // have it connect to a port - await connectDialog.connectTo(appFixture.serviceUri); - - // make sure the connect dialog becomes hidden - await waitFor(() async => !(await connectDialog.isVisible())); - }); -} - -class ConnectDialogManager { - ConnectDialogManager(this.tools); - - final DevtoolsManager tools; - - Future isVisible() async { - final response = await tools.tabInstance.send('connectDialog.isVisible'); - return response.result as bool; - } - - Future connectTo(Uri uri) async { - // We have to convert to String here as this goes over JSON. - await tools.tabInstance.send('connectDialog.connectTo', uri.toString()); - } -} diff --git a/packages/devtools_app/test/legacy_integration_tests/debugger.dart b/packages/devtools_app/test/legacy_integration_tests/debugger.dart deleted file mode 100644 index 420b258ff07..00000000000 --- a/packages/devtools_app/test/legacy_integration_tests/debugger.dart +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright 2019 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. - -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/test_infra/fixtures/debugging_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); - - // 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 scripts = await debuggingManager.getScripts(); - expect(scripts, isNotEmpty); - expect(scripts, anyElement(endsWith(appFixture.appScriptPath))); - }); - - test('breakpoints, variables, resume', () async { - appFixture = await CliAppFixture.create( - 'test/test_infra/fixtures/debugging_app.dart', - ); - - final source = appFixture.scriptSource; - final breakpointLines = CliAppFixture.parseBreakpointLines(source); - - 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); - - // clear and verify breakpoints - List breakpoints = await debuggingManager.getBreakpoints(); - expect(breakpoints, isEmpty); - - await delay(); - - // set and verify breakpoints - for (final 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 frames = await debuggingManager.getCallStackFrames(); - expect(frames.length, greaterThan(2)); - expect(frames.sublist(0, 2), [ - 'Cat.performAction:debugging_app.dart', - 'main.run.: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/test_infra/fixtures/debugging_app_async.dart', - ); - - final source = appFixture.scriptSource; - final breakpointLine = CliAppFixture.parseBreakpointLines(source).single; - final steppingLines = CliAppFixture.parseSteppingLines(source); - - 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); - - // clear and verify breakpoints - List 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 (final 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 frames = await debuggingManager.getCallStackFrames(); - expect(frames.length, greaterThan(4)); - expect(frames.sublist(0, 4), [ - 'performAction:debugging_app_async.dart', - '', - 'main.run.:debugging_app_async.dart', - '', - ]); - - // 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/test_infra/fixtures/debugging_app_exception.dart', - ); - - final source = appFixture.scriptSource; - final exceptionLine = CliAppFixture.parseExceptionLines(source).first; - - 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); - - // 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(), [ - ':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/test_infra/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' - '0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 \n' - '8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 \n' - '\n' - ' 16 16 19 19 \n' - ' 34 34 37 37 \n' - '\n' - ' 124 124 127 127 \n' - ' 142 142 145 145 \n' - '\n' - ' 232 232 233 233 234 234 235 235 236 236 237 237 238 238 239 239 \n' - ' 240 240 241 241 242 242 243 243 244 244 245 245 246 246 247 247 \n' - ' 248 248 249 249 250 250 251 251 252 252 253 253 254 254 255 255 \n', - ), - ); - }); - - test('pause', () async { - appFixture = await CliAppFixture.create( - 'test/test_infra/fixtures/debugging_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); - - // 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 resume() async { - await tools.tabInstance.send('debugger.resume'); - } - - Future pause() async { - await tools.tabInstance.send('debugger.pause'); - } - - Future step() async { - await tools.tabInstance.send('debugger.step'); - } - - Future getLocation() async { - final response = await tools.tabInstance.send('debugger.getLocation'); - return response.result as String?; - } - - Future> getVariables() async { - final response = await tools.tabInstance.send('debugger.getVariables'); - final result = response.result as List; - return result.cast(); - } - - Future getState() async { - final response = await tools.tabInstance.send('debugger.getState'); - return response.result as String?; - } - - Future getConsoleContents() async { - final response = await tools.tabInstance.send( - 'debugger.getConsoleContents', - ); - return response.result as String?; - } - - Future clearBreakpoints() async { - await tools.tabInstance.send('debugger.clearBreakpoints'); - } - - Future addBreakpoint(String path, int line) async { - await tools.tabInstance.send('debugger.addBreakpoint', [path, line]); - } - - Future setIsolatePauseMode(String mode) async { - await tools.tabInstance.send('debugger.setIsolatePauseMode', mode); - } - - Future> getBreakpoints() async { - final response = await tools.tabInstance.send('debugger.getBreakpoints'); - final result = response.result as List; - return result.cast(); - } - - Future> getScripts() async { - final response = await tools.tabInstance.send('debugger.getScripts'); - final result = response.result as List; - return result.cast(); - } - - Future supportsScripts() async { - final response = await tools.tabInstance.send('debugger.supportsScripts'); - return response.result as bool; - } - - Future> getCallStackFrames() async { - final response = await tools.tabInstance.send( - 'debugger.getCallStackFrames', - ); - final result = response.result as List; - return result.cast(); - } -} diff --git a/packages/devtools_app/test/legacy_integration_tests/integration.dart b/packages/devtools_app/test/legacy_integration_tests/integration.dart deleted file mode 100644 index eadfcfb2591..00000000000 --- a/packages/devtools_app/test/legacy_integration_tests/integration.dart +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2018 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. - -// ignore_for_file: avoid_print - -@TestOn('vm') -library; - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:devtools_shared/devtools_shared.dart'; -import 'package:devtools_shared/devtools_test_utils.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' - show ConsoleAPIEvent, RemoteObject; - -const verboseTesting = false; - -late WebBuildFixture webBuildFixture; -late BrowserManager browserManager; - -class DevtoolsManager { - DevtoolsManager(this.tabInstance, this.baseUri); - - final BrowserTabInstance tabInstance; - final Uri baseUri; - - Future start( - AppFixture appFixture, { - Uri? overrideUri, - bool waitForConnection = true, - }) async { - final baseAppUri = baseUri.resolve( - 'index.html?uri=${Uri.encodeQueryComponent(appFixture.serviceUri.toString())}', - ); - await tabInstance.tab.navigate('${overrideUri ?? baseAppUri}'); - - // wait for app initialization - await Future.wait([ - waitForConnection - ? tabInstance.onEvent.firstWhere( - (msg) => msg.event == 'app.devToolsReady', - ) - : Future.value(), - tabInstance.getBrowserChannel(), - ]); - } - - Future switchPage(String page) async { - await tabInstance.send('switchPage', page); - } - - Future currentPageId() async { - final response = await tabInstance.send('currentPageId'); - return response.result as String?; - } -} - -class BrowserManager { - BrowserManager._(this.chromeProcess, this.tab); - - static Future create() async { - final chrome = Chrome.locate(); - if (chrome == null) { - throw 'unable to locate Chrome'; - } - - final chromeProcess = await chrome.start(); - final tab = (await chromeProcess.getFirstTab())!; - - await tab.connect(); - - return BrowserManager._(chromeProcess, tab); - } - - final ChromeProcess chromeProcess; - final ChromeTab tab; - - Future createNewTab() async { - final targetId = await this.tab.createNewTarget(); - - await delay(); - - final tab = (await chromeProcess.connectToTabId('localhost', targetId))!; - await tab.connect(verbose: true); - - await delay(); - - await tab.wipConnection!.target.activateTarget(targetId); - - await delay(); - - return BrowserTabInstance(tab); - } - - void teardown() { - chromeProcess.kill(); - } -} - -class BrowserTabInstance { - BrowserTabInstance(this.tab) { - tab.onConsoleAPICalled - .where((ConsoleAPIEvent event) => event.type == 'log') - .listen((ConsoleAPIEvent event) { - if (event.args.isNotEmpty) { - final message = event.args.first; - final value = '${message.value}'; - if (value.startsWith('[') && value.endsWith(']')) { - try { - final msg = jsonDecode(value.substring(1, value.length - 1)); - if (msg is Map) { - _handleBrowserMessage(msg); - } - } catch (_) { - // ignore - } - } - } - }); - } - - final ChromeTab tab; - - RemoteObject? _remote; - - Future getBrowserChannel() async { - final start = DateTime.now(); - final end = start.add(const Duration(seconds: 30)); - - while (true) { - try { - return await _getAppChannelObject(); - } catch (e) { - if (end.isBefore(DateTime.now())) { - final duration = DateTime.now().difference(start); - print('timeout getting the browser channel object ($duration)'); - rethrow; - } - } - - await Future.delayed(const Duration(milliseconds: 25)); - } - } - - Future _getAppChannelObject() { - return tab.wipConnection!.runtime.evaluate('devtools'); - } - - int _nextId = 1; - - final _completers = >{}; - - final _eventStream = StreamController.broadcast(); - - Stream get onEvent => _eventStream.stream; - - Future send(String method, [Object? params]) async { - _remote ??= await _getAppChannelObject(); - - final id = _nextId++; - - final completer = Completer(); - _completers[id] = completer; - - try { - await tab.wipConnection!.runtime.callFunctionOn( - "function (method, id, params) { return window['devtools'].send(method, id, params); }", - objectId: _remote!.objectId, - arguments: [method, id, params], - ); - - return completer.future; - } catch (e, st) { - _completers.remove(id); - completer.completeError(e, st); - rethrow; - } - } - - Future close() async { - // In Headless Chrome, we get Inspector.detached when we close the last - // target rather than a response. - await Future.any(>[ - tab.wipConnection!.onNotification.firstWhere( - (n) => n.method == 'Inspector.detached', - ), - tab.wipConnection!.target.closeTarget(tab.wipTab.id), - ]); - } - - void _handleBrowserMessage(Map message) { - if (verboseTesting) { - print(message); - } - - if (message.containsKey('id')) { - // handle a response: {id: 1} - final response = AppResponse(message); - final completer = _completers.remove(response.id)!; - if (response.hasError) { - completer.completeError(response.error); - } else { - completer.complete(response); - } - } else { - // handle an event: {event: app.echo, params: foo} - _eventStream.add(AppEvent(message)); - } - } -} - -class AppEvent { - AppEvent(this.json); - - final Map json; - - String? get event => json['event']; - - Object? get params => json['params']; - - @override - String toString() => '$event ${params ?? ''}'; -} - -class AppResponse { - AppResponse(this.json); - - final Map json; - - int? get id => json['id']; - - Object? get result => json['result']; - - bool get hasError => json.containsKey('error'); - - AppError get error => AppError(json['error']); - - @override - String toString() { - return hasError ? error.toString() : result.toString(); - } -} - -class AppError { - AppError(this.json); - - final Map json; - - String? get message => json['message']; - - String? get stackTrace => json['stackTrace']; - - @override - String toString() => '$message\n$stackTrace'; -} - -class WebBuildFixture { - WebBuildFixture._(this.process, this.url, this.verbose); - - static Future serve({ - bool release = false, - bool verbose = false, - }) async { - final cliArgs = [ - 'pub', - 'run', - 'build_runner', - 'serve', - 'web', - '--delete-conflicting-outputs', - ]; - if (release) { - cliArgs.add('--release'); - } - - final process = await _runFlutter(cliArgs); - - final hasUrl = Completer(); - - _toLines(process.stderr).listen((String line) { - if (verbose || hasUrl.isCompleted) { - print( - 'pub run build_runner serve • ${process.pid}' - ' • STDERR • ${line.trim()}', - ); - } - - final err = 'error starting webdev: $line'; - if (!hasUrl.isCompleted) { - hasUrl.completeError(err); - } else { - print('Ignoring stderr output because already completed'); - } - }); - - _toLines(process.stdout).listen((String line) { - if (verbose) { - print('pub run build_runner serve • ${process.pid} • ${line.trim()}'); - } - - // Serving `web` on http://localhost:8080 - if (line.contains('Serving `web`')) { - hasUrl.safeComplete( - line.substring(line.indexOf('http://')), - () => print( - 'Ignoring "Serving..." notification because already completed', - ), - ); - } - }); - - final url = await hasUrl.future; - - await delay(); - - return WebBuildFixture._(process, url, verbose); - } - - static Future build({bool verbose = false}) async { - final clean = await _runFlutter(['clean']); - expect(await clean.exitCode, 0); - final pubGet = await _runFlutter(['pub', 'get']); - expect(await pubGet.exitCode, 0); - - final cliArgs = []; - String commandName; - commandName = 'flutter build web'; - cliArgs.addAll([ - 'build', - 'web', - '--pwa-strategy=none', - '--dart-define=FLUTTER_WEB_USE_SKIA=true', - '--no-tree-shake-icons', - ]); - - final process = await _runFlutter(cliArgs, verbose: verbose); - - final buildFinished = Completer(); - - _toLines(process.stderr).listen((String line) { - // TODO(https://github.com/flutter/devtools/issues/2477): this is a - // work around for an expected warning that would otherwise fail the test. - if (line.toLowerCase().contains('warning')) { - return; - } - if (line.toLowerCase().contains(' from path ../devtools_')) { - return; - } - - final err = 'error building flutter: $line'; - if (!buildFinished.isCompleted) { - buildFinished.completeError(err); - } else { - print(err); - } - }); - - _toLines(process.stdout).listen((String line) { - if (verbose) { - print('$commandName • ${line.trim()}'); - } - - if (!buildFinished.isCompleted) { - if (line.contains('[INFO] Succeeded')) { - buildFinished.complete(); - } else if (line.contains('[SEVERE]')) { - buildFinished.completeError(line); - } - } - }); - - unawaited( - process.exitCode.then((code) { - if (!buildFinished.isCompleted) { - if (code == 0) { - buildFinished.complete(); - } else { - buildFinished.completeError('Exited with code $code'); - } - } - }), - ); - - await buildFinished.future.catchError((Object? e) { - fail('Build failed: $e'); - }); - - await process.exitCode; - } - - final Process process; - final String url; - final bool verbose; - - Uri get baseUri => Uri.parse(url); - - Future teardown() async { - process.kill(); - final exitCode = await process.exitCode; - if (verbose) { - print('flutter exited with code $exitCode'); - } - } - - static Future _runFlutter( - List buildArgs, { - bool verbose = false, - }) { - // Remove the DART_VM_OPTIONS env variable from the child process, so the - // Dart VM doesn't try and open a service protocol port if - // 'DART_VM_OPTIONS: --enable-vm-service:63990' was passed in. - final environment = Map.from(Platform.environment); - if (environment.containsKey('DART_VM_OPTIONS')) { - environment['DART_VM_OPTIONS'] = ''; - } - - // TODO(https://github.com/flutter/devtools/issues/1145): The pub-based - // version of this code would run a pub snapshot instead of starting pub - // directly to prevent Windows-based test runs getting killed but leaving - // the pub process behind. Something similar might be needed here. - // See here for more information: - // https://github.com/flutter/flutter/blob/master/docs/tool/README.md#debugging-the-flutter-command-line-tool - final executable = Platform.isWindows ? 'flutter.bat' : 'flutter'; - - if (verbose) { - print( - 'Running "$executable" with args: ${buildArgs.join(' ')} from ${Directory.current.path}', - ); - } - return Process.start( - executable, - buildArgs, - environment: environment, - workingDirectory: Directory.current.path, - ); - } - - static Stream _toLines(Stream> stream) => - stream.transform(utf8.decoder).transform(const LineSplitter()); -} diff --git a/packages/devtools_app/test/legacy_integration_tests/integration_test.dart b/packages/devtools_app/test/legacy_integration_tests/integration_test.dart deleted file mode 100644 index 10b791db5f1..00000000000 --- a/packages/devtools_app/test/legacy_integration_tests/integration_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. - -@TestOn('vm') -library; - -import 'dart:io'; - -import 'package:flutter_test/flutter_test.dart'; - -import 'app.dart'; -import 'debugger.dart'; -import 'integration.dart'; -import 'logging.dart'; - -void main() { - group('integration', () { - // TODO(#1965): fix and re-enable integration tests. - // ignore: dead_code - if (false) { - setUpAll(() async { - final testInReleaseMode = - Platform.environment['WEBDEV_RELEASE'] == 'true'; - - webBuildFixture = await WebBuildFixture.serve( - release: testInReleaseMode, - verbose: true, - ); - browserManager = await BrowserManager.create(); - }); - - tearDownAll(() async { - browserManager.teardown(); - await webBuildFixture.teardown(); - }); - - group('app', appTests, skip: true); - group('logging', loggingTests, skip: true); - // Temporarily skip tests. See https://github.com/flutter/devtools/issues/1343. - group('debugging', debuggingTests, skip: true); - } - }); -} diff --git a/packages/devtools_app/test/legacy_integration_tests/logging.dart b/packages/devtools_app/test/legacy_integration_tests/logging.dart deleted file mode 100644 index 39d0eb5cdf5..00000000000 --- a/packages/devtools_app/test/legacy_integration_tests/logging.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 The Flutter Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. - -import 'package:devtools_shared/devtools_test_utils.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'integration.dart'; - -void loggingTests() { - late CliAppFixture appFixture; - late BrowserTabInstance tabInstance; - - setUp(() async { - appFixture = await CliAppFixture.create( - 'test/test_infra/fixtures/logging_app.dart', - ); - tabInstance = await browserManager.createNewTab(); - }); - - tearDown(() async { - await tabInstance.close(); - await appFixture.teardown(); - }); - - test('displays log data', () async { - final tools = DevtoolsManager(tabInstance, webBuildFixture.baseUri); - await tools.start(appFixture); - await tools.switchPage('logging'); - - final currentPageId = await tools.currentPageId(); - expect(currentPageId, 'logging'); - - // Cause app to log. - final logs = LoggingManager(tools); - await logs.clearLogs(); - expect(await logs.logCount(), 0); - await appFixture.invoke('controller.emitLog()'); - - // Verify the log data shows up in the UI. - await waitFor(() async => (await logs.logCount()) > 0); - expect(await logs.logCount(), greaterThan(0)); - }); - - test('log screen postpones write when offscreen', () async { - final tools = DevtoolsManager(tabInstance, webBuildFixture.baseUri); - await tools.start(appFixture); - await tools.switchPage('logging'); - - final currentPageId = await tools.currentPageId(); - expect(currentPageId, 'logging'); - - final logs = LoggingManager(tools); - - // Verify that the log is empty. - expect(await logs.logCount(), 0); - - // Switch to a different page. - await tools.switchPage('performance'); - - // Cause app to log. - await appFixture.invoke('controller.emitLog()'); - - // Verify that the log is empty. - expect(await logs.logCount(), 0); - - // Switch to the logs page. - await tools.switchPage('logging'); - - // Verify the log data shows up in the UI. - await waitFor(() async => (await logs.logCount()) > 0); - expect(await logs.logCount(), greaterThan(0)); - }); -} - -class LoggingManager { - LoggingManager(this.tools); - - final DevtoolsManager tools; - - Future clearLogs() async { - await tools.tabInstance.send('logging.clearLogs'); - } - - Future logCount() async { - final response = await tools.tabInstance.send('logging.logCount'); - return response.result as int; - } -} diff --git a/packages/devtools_app/test/legacy_integration_tests/readme.md b/packages/devtools_app/test/legacy_integration_tests/readme.md deleted file mode 100644 index f2e1f5a2fdc..00000000000 --- a/packages/devtools_app/test/legacy_integration_tests/readme.md +++ /dev/null @@ -1,30 +0,0 @@ - -## plan for integration tests - -Integration tests are expected to be heavyweight and test broad areas of a -use case. We likely wouldn't have more than 1-2 dozen of them. - -### startup - -- start a Dart VM or a Flutter app on a sample app -- connect to it via a service protocol connection -- start a chrome process -- connect to it via a chrome debug protocol connection -- start a web serve serving a debug build of the devtools app -- switch the chrome tab page to the devtools app w/ the port of the running dart/flutter app -- wait for app initialization - -### testing -- interact with devtools by invoking methods on objects registered globally -- verify expected behavior by invoking methods, or listening to events written - to the browser's console - -### teardown - -- tear down chrome -- tear down the web server -- tear down the running Dart/Flutter app diff --git a/packages/devtools_app/test/screens/logging/metadata_test.dart b/packages/devtools_app/test/screens/logging/metadata_test.dart index 2fa6dcd2f84..55b9f82d374 100644 --- a/packages/devtools_app/test/screens/logging/metadata_test.dart +++ b/packages/devtools_app/test/screens/logging/metadata_test.dart @@ -97,7 +97,7 @@ void main() { return Flexible( child: Padding( padding: const EdgeInsets.all(8.0), - child: MetadataChips(data: log, maxWidth: 1000.0), + child: MetadataChips(data: log), ), ); }).toList(), diff --git a/packages/devtools_app/web/index.html b/packages/devtools_app/web/index.html index 060532d9571..55fd1e0b878 100644 --- a/packages/devtools_app/web/index.html +++ b/packages/devtools_app/web/index.html @@ -55,7 +55,6 @@ } - diff --git a/packages/devtools_app/web/styles.css b/packages/devtools_app/web/styles.css index 513160a210d..909f566decf 100644 --- a/packages/devtools_app/web/styles.css +++ b/packages/devtools_app/web/styles.css @@ -3,11 +3,6 @@ Copyright 2025 The Flutter Authors Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. **/ -@media (-webkit-max-device-pixel-ratio: 1) { - * { - image-rendering: pixelated; - } -} body { overscroll-behavior-x: none; diff --git a/packages/devtools_app_shared/lib/src/ui/theme/theme.dart b/packages/devtools_app_shared/lib/src/ui/theme/theme.dart index 9ddfbde4451..f20040295d7 100644 --- a/packages/devtools_app_shared/lib/src/ui/theme/theme.dart +++ b/packages/devtools_app_shared/lib/src/ui/theme/theme.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. -// TODO(kenz): remove once min flutter version of devtools_app_shared >= 3.25 -// ignore_for_file: deprecated_member_use, analysis performed with newer flutter version than min sdk - import 'package:flutter/material.dart'; import '../ui_utils.dart'; @@ -231,9 +228,10 @@ const darkColorScheme = ColorScheme( ); const searchMatchColor = Colors.yellow; -final searchMatchColorOpaque = Colors.yellow.withOpacity(0.5); +final searchMatchColorOpaque = Colors.yellow.withValues(alpha: 255 / 2); const activeSearchMatchColor = Colors.orangeAccent; -final activeSearchMatchColorOpaque = Colors.orangeAccent.withOpacity(0.5); +final activeSearchMatchColorOpaque = + Colors.orangeAccent.withValues(alpha: 255 / 2); /// Gets an alternating color to use for indexed UI elements. Color alternatingColorForIndex(int index, ColorScheme colorScheme) { diff --git a/packages/devtools_app_shared/lib/src/utils/utils.dart b/packages/devtools_app_shared/lib/src/utils/utils.dart index 62d7e272600..df6db6176cf 100644 --- a/packages/devtools_app_shared/lib/src/utils/utils.dart +++ b/packages/devtools_app_shared/lib/src/utils/utils.dart @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. -// TODO(kenz): remove once min flutter version of devtools_app_shared >= 3.25 -// ignore_for_file: deprecated_member_use, analysis performed with newer flutter version than min sdk - import 'dart:async'; import 'dart:math'; @@ -119,8 +116,9 @@ String toCssHexColor(Color color) { // In CSS Hex, Alpha comes last, but in Flutter's `value` field, alpha is // in the high bytes, so just using `value.toRadixString(16)` will put alpha // in the wrong position. - String hex(int val) => val.toRadixString(16).padLeft(2, '0'); - return '#${hex(color.red)}${hex(color.green)}${hex(color.blue)}${hex(color.alpha)}'; + String hex(double channelValue) => + (channelValue * 255).round().toRadixString(16).padLeft(2, '0'); + return '#${hex(color.r)}${hex(color.g)}${hex(color.b)}${hex(color.a)}'; } extension StringUtilities on String {