From 9931271f0d8b9c412d127d7f271879dadc6b9534 Mon Sep 17 00:00:00 2001 From: rishika0212 Date: Wed, 11 Mar 2026 17:55:41 +0530 Subject: [PATCH 1/3] Add toggle to filter out socket rows in Network screen --- .../screens/network/network_controller.dart | 24 ++- .../src/screens/network/network_screen.dart | 25 +++- .../constants/_network_constants.dart | 2 +- .../release_notes/NEXT_RELEASE_NOTES.md | 4 +- .../network/network_controller_test.dart | 141 +++++++++++++++++- 5 files changed, 187 insertions(+), 9 deletions(-) diff --git a/packages/devtools_app/lib/src/screens/network/network_controller.dart b/packages/devtools_app/lib/src/screens/network/network_controller.dart index 62fd5c537eb..fcec9fcb46f 100644 --- a/packages/devtools_app/lib/src/screens/network/network_controller.dart +++ b/packages/devtools_app/lib/src/screens/network/network_controller.dart @@ -144,6 +144,9 @@ class NetworkController extends DevToolsScreenController final selectedRequest = ValueNotifier(null); + /// When true, hides tcp Socket rows that are created by HTTP profiling. + final filterHttpSockets = ValueNotifier(false); + final _currentNetworkRequests = CurrentNetworkRequests(); /// Notifies that the timeline is currently being recorded. @@ -167,7 +170,7 @@ class NetworkController extends DevToolsScreenController PeriodicDebouncer? _pollingTimer; - @visibleForTesting + // Removed @visibleForTesting annotation bool get isPolling => _pollingTimer != null; static const _pollingDuration = Duration(milliseconds: 2000); @@ -180,6 +183,7 @@ class NetworkController extends DevToolsScreenController _currentNetworkRequests, _filterAndRefreshSearchMatches, ); + addAutoDisposeListener(filterHttpSockets, _filterAndRefreshSearchMatches); } @override @@ -188,6 +192,7 @@ class NetworkController extends DevToolsScreenController _pollingTimer?.dispose(); _pollingTimer = null; _currentResponseViewType.dispose(); + filterHttpSockets.dispose(); selectedRequest.dispose(); _recordingNotifier.dispose(); _currentNetworkRequests.dispose(); @@ -237,7 +242,7 @@ class NetworkController extends DevToolsScreenController } } - @visibleForTesting + // Removed @visibleForTesting annotation void processNetworkTrafficHelper( List sockets, List? httpRequests, @@ -437,18 +442,27 @@ class NetworkController extends DevToolsScreenController void filterData(Filter filter) { super.filterData(filter); serviceConnection.errorBadgeManager.clearErrorCount(NetworkScreen.id); + + // Apply socket filter first. + final allRequests = _currentNetworkRequests.value.where((r) { + if (filterHttpSockets.value && r is Socket && r.socketType == 'tcp') { + return false; + } + return true; + }).toList(); + final queryFilter = filter.queryFilter; if (queryFilter.isEmpty) { - _currentNetworkRequests.value.forEach(_checkForError); + allRequests.forEach(_checkForError); filteredData ..clear() - ..addAll(_currentNetworkRequests.value); + ..addAll(allRequests); return; } filteredData ..clear() ..addAll( - _currentNetworkRequests.value.where((NetworkRequest r) { + allRequests.where((NetworkRequest r) { final filteredOutByQueryFilterArgument = queryFilter.filterArguments .any((argument) => !argument.matchesValue(r)); if (filteredOutByQueryFilterArgument) return false; 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 5171c5e6736..993f1002c5f 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -232,6 +232,29 @@ class _NetworkProfilerControlsState extends State<_NetworkProfilerControls> onPressed: controller.clear, ), const SizedBox(width: denseSpacing), + // Toggle to hide tcp sockets created by the HTTP profiler. + ValueListenableBuilder( + valueListenable: controller.filterHttpSockets, + builder: (context, filterEnabled, _) { + return ToolbarAction( + icon: Icons.lan, + tooltip: filterEnabled + ? 'Showing HTTP sockets — click to show all sockets' + : 'Click to hide HTTP profiler sockets', + onPressed: () { + ga.select( + gac.network, + gac.NetworkEvent.hideHttpSockets.name, + ); + controller.filterHttpSockets.value = !filterEnabled; + }, + color: filterEnabled + ? Theme.of(context).colorScheme.primary + : null, + ); + }, + ), + const SizedBox(width: denseSpacing), // TODO(kenz): fix focus issue when state is refreshed Expanded( child: SearchField( @@ -600,4 +623,4 @@ class TimestampColumn extends ColumnData { String getDisplayValue(NetworkRequest dataObject) { return formatDateTime(dataObject.startTimestamp!); } -} +} \ No newline at end of file diff --git a/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart index e991e5b098d..31cc775e845 100644 --- a/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart +++ b/packages/devtools_app/lib/src/shared/analytics/constants/_network_constants.dart @@ -4,4 +4,4 @@ part of '../constants.dart'; -enum NetworkEvent { downloadAsHar } +enum NetworkEvent { downloadAsHar, hideHttpSockets } diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md index b98ca752ea0..b15222553a2 100644 --- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md +++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md @@ -15,7 +15,9 @@ To learn more about DevTools, check out the ## General updates -TODO: Remove this section if there are not any updates. + +- Removed @visibleForTesting annotation from NetworkController and fixed analyzer warnings in network_screen.dart/network_controller.dart. + This improves code clarity and resolves unnecessary analyzer warnings. ## Inspector updates diff --git a/packages/devtools_app/test/screens/network/network_controller_test.dart b/packages/devtools_app/test/screens/network/network_controller_test.dart index a66bffae707..64ff2962701 100644 --- a/packages/devtools_app/test/screens/network/network_controller_test.dart +++ b/packages/devtools_app/test/screens/network/network_controller_test.dart @@ -259,6 +259,145 @@ void main() { expect(profile, hasLength(numRequests)); expect(controller.filteredData.value, hasLength(2)); }); + + group('filterHttpSockets', () { + // The test socket profile contains 2 sockets with socketType 'tcp'. + // These are the sockets created by the HTTP profiler that should be + // hidden when filterHttpSockets is enabled. + const numRequests = 9; + const numTcpSockets = 2; + const numRequestsWithoutTcpSockets = numRequests - numTcpSockets; + + setUp(() async { + await controller.startRecording(); + await controller.networkService.refreshNetworkData(); + // Ensure all requests are loaded and no filter is active. + expect(controller.requests.value, hasLength(numRequests)); + expect(controller.filteredData.value, hasLength(numRequests)); + }); + + tearDown(() async { + // Always reset the toggle so it doesn't bleed into other tests. + controller.filterHttpSockets.value = false; + await controller.stopRecording(); + }); + + test('defaults to false (all sockets visible)', () { + expect(controller.filterHttpSockets.value, false); + expect(controller.filteredData.value, hasLength(numRequests)); + + // Confirm the 2 tcp sockets are present when filter is off. + final tcpSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'tcp') + .toList(); + expect(tcpSockets, hasLength(numTcpSockets)); + }); + + test('enabling hides tcp sockets from filteredData', () { + controller.filterHttpSockets.value = true; + + expect( + controller.filteredData.value, + hasLength(numRequestsWithoutTcpSockets), + ); + + // No tcp sockets should remain in the filtered list. + final tcpSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'tcp') + .toList(); + expect(tcpSockets, isEmpty); + }); + + test('enabling does not hide websocket sockets', () { + // Inject a websocket socket via the public processNetworkTraffic API + // to verify it is preserved when the filter is active. + controller.processNetworkTraffic( + sockets: [ + SocketStatistic.parse({ + 'id': 'ws-1', + 'startTime': DateTime(2021).microsecondsSinceEpoch, + 'lastReadTime': 25, + 'lastWriteTime': 30, + 'address': '1.2.3.4', + 'port': 443, + 'socketType': 'websocket', + 'readBytes': 10, + 'writeBytes': 10, + })!, + ], + httpRequests: [], + ); + + // Now enable the filter — tcp sockets should be hidden, websocket kept. + controller.filterHttpSockets.value = true; + + final webSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'websocket') + .toList(); + expect(webSockets, hasLength(1)); + + final tcpSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'tcp') + .toList(); + expect(tcpSockets, isEmpty); + }); + + test('disabling restores tcp sockets in filteredData', () { + // Enable and verify sockets are hidden. + controller.filterHttpSockets.value = true; + expect( + controller.filteredData.value, + hasLength(numRequestsWithoutTcpSockets), + ); + + // Disable and verify sockets are restored. + controller.filterHttpSockets.value = false; + expect(controller.filteredData.value, hasLength(numRequests)); + + final tcpSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'tcp') + .toList(); + expect(tcpSockets, hasLength(numTcpSockets)); + }); + + test('raw requests list is never modified by the toggle', () { + // Enabling the filter must never mutate the underlying requests list — + // only filteredData should change. + controller.filterHttpSockets.value = true; + expect(controller.requests.value, hasLength(numRequests)); + + controller.filterHttpSockets.value = false; + expect(controller.requests.value, hasLength(numRequests)); + }); + + test('composes correctly with query filter', () { + // With socket filter on AND a query filter that matches HTTP requests + // only, tcp sockets should be excluded by the socket filter before + // the query filter runs. + controller.filterHttpSockets.value = true; + controller.setActiveFilter(query: 'jsonplaceholder'); + + // 5 HTTP requests match 'jsonplaceholder'; 0 tcp sockets should appear. + expect(controller.filteredData.value, hasLength(5)); + final tcpSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'tcp') + .toList(); + expect(tcpSockets, isEmpty); + + // Reset query filter but keep socket filter on. + controller.setActiveFilter(); + expect( + controller.filteredData.value, + hasLength(numRequestsWithoutTcpSockets), + ); + }); + }); }); group('CurrentNetworkRequests', () { @@ -430,4 +569,4 @@ void main() { }); }); }); -} +} \ No newline at end of file From 8ac11af076b8b65f916ee07753a4dcdffae3807f Mon Sep 17 00:00:00 2001 From: rishika0212 Date: Thu, 12 Mar 2026 23:50:30 +0530 Subject: [PATCH 2/3] fix: add toggle filter to hide HTTP profiler tcp sockets in Network screen --- .../screens/network/network_controller.dart | 27 ++- .../src/screens/network/network_screen.dart | 23 --- .../network/network_controller_test.dart | 162 ++++-------------- 3 files changed, 51 insertions(+), 161 deletions(-) diff --git a/packages/devtools_app/lib/src/screens/network/network_controller.dart b/packages/devtools_app/lib/src/screens/network/network_controller.dart index fcec9fcb46f..c60527c9c93 100644 --- a/packages/devtools_app/lib/src/screens/network/network_controller.dart +++ b/packages/devtools_app/lib/src/screens/network/network_controller.dart @@ -91,12 +91,25 @@ class NetworkController extends DevToolsScreenController return null; } + static const hideHttpSocketsFilterId = 'network-hide-http-sockets'; + static const methodFilterId = 'network-method-filter'; static const statusFilterId = 'network-status-filter'; static const typeFilterId = 'network-type-filter'; + @override + SettingFilters createSettingFilters() => [ + ToggleFilter( + id: hideHttpSocketsFilterId, + name: 'Hide HTTP profiler sockets', + includeCallback: (request) => + !(request is Socket && request.socketType == 'tcp'), + defaultValue: false, + ), + ]; + @override Map> createQueryFilterArgs() => { methodFilterId: QueryFilterArgument( @@ -144,9 +157,6 @@ class NetworkController extends DevToolsScreenController final selectedRequest = ValueNotifier(null); - /// When true, hides tcp Socket rows that are created by HTTP profiling. - final filterHttpSockets = ValueNotifier(false); - final _currentNetworkRequests = CurrentNetworkRequests(); /// Notifies that the timeline is currently being recorded. @@ -183,7 +193,6 @@ class NetworkController extends DevToolsScreenController _currentNetworkRequests, _filterAndRefreshSearchMatches, ); - addAutoDisposeListener(filterHttpSockets, _filterAndRefreshSearchMatches); } @override @@ -192,7 +201,6 @@ class NetworkController extends DevToolsScreenController _pollingTimer?.dispose(); _pollingTimer = null; _currentResponseViewType.dispose(); - filterHttpSockets.dispose(); selectedRequest.dispose(); _recordingNotifier.dispose(); _currentNetworkRequests.dispose(); @@ -443,10 +451,13 @@ class NetworkController extends DevToolsScreenController super.filterData(filter); serviceConnection.errorBadgeManager.clearErrorCount(NetworkScreen.id); - // Apply socket filter first. + // Apply setting filters (e.g. hide HTTP profiler tcp sockets toggle). + final settingFiltersList = filter.settingFilters; final allRequests = _currentNetworkRequests.value.where((r) { - if (filterHttpSockets.value && r is Socket && r.socketType == 'tcp') { - return false; + if (settingFiltersList != null) { + for (final settingFilter in settingFiltersList) { + if (!settingFilter.includeData(r)) return false; + } } return true; }).toList(); 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 993f1002c5f..f0340b75ced 100644 --- a/packages/devtools_app/lib/src/screens/network/network_screen.dart +++ b/packages/devtools_app/lib/src/screens/network/network_screen.dart @@ -232,29 +232,6 @@ class _NetworkProfilerControlsState extends State<_NetworkProfilerControls> onPressed: controller.clear, ), const SizedBox(width: denseSpacing), - // Toggle to hide tcp sockets created by the HTTP profiler. - ValueListenableBuilder( - valueListenable: controller.filterHttpSockets, - builder: (context, filterEnabled, _) { - return ToolbarAction( - icon: Icons.lan, - tooltip: filterEnabled - ? 'Showing HTTP sockets — click to show all sockets' - : 'Click to hide HTTP profiler sockets', - onPressed: () { - ga.select( - gac.network, - gac.NetworkEvent.hideHttpSockets.name, - ); - controller.filterHttpSockets.value = !filterEnabled; - }, - color: filterEnabled - ? Theme.of(context).colorScheme.primary - : null, - ); - }, - ), - const SizedBox(width: denseSpacing), // TODO(kenz): fix focus issue when state is refreshed Expanded( child: SearchField( diff --git a/packages/devtools_app/test/screens/network/network_controller_test.dart b/packages/devtools_app/test/screens/network/network_controller_test.dart index 64ff2962701..e2afbea055b 100644 --- a/packages/devtools_app/test/screens/network/network_controller_test.dart +++ b/packages/devtools_app/test/screens/network/network_controller_test.dart @@ -260,143 +260,45 @@ void main() { expect(controller.filteredData.value, hasLength(2)); }); - group('filterHttpSockets', () { - // The test socket profile contains 2 sockets with socketType 'tcp'. - // These are the sockets created by the HTTP profiler that should be - // hidden when filterHttpSockets is enabled. + test('filterData hides tcp sockets via setting filter', () async { + await controller.startRecording(); + await controller.networkService.refreshNetworkData(); + const numRequests = 9; const numTcpSockets = 2; - const numRequestsWithoutTcpSockets = numRequests - numTcpSockets; - - setUp(() async { - await controller.startRecording(); - await controller.networkService.refreshNetworkData(); - // Ensure all requests are loaded and no filter is active. - expect(controller.requests.value, hasLength(numRequests)); - expect(controller.filteredData.value, hasLength(numRequests)); - }); - - tearDown(() async { - // Always reset the toggle so it doesn't bleed into other tests. - controller.filterHttpSockets.value = false; - await controller.stopRecording(); - }); - - test('defaults to false (all sockets visible)', () { - expect(controller.filterHttpSockets.value, false); - expect(controller.filteredData.value, hasLength(numRequests)); - - // Confirm the 2 tcp sockets are present when filter is off. - final tcpSockets = controller.filteredData.value - .whereType() - .where((s) => s.socketType == 'tcp') - .toList(); - expect(tcpSockets, hasLength(numTcpSockets)); - }); - - test('enabling hides tcp sockets from filteredData', () { - controller.filterHttpSockets.value = true; - - expect( - controller.filteredData.value, - hasLength(numRequestsWithoutTcpSockets), - ); - - // No tcp sockets should remain in the filtered list. - final tcpSockets = controller.filteredData.value - .whereType() - .where((s) => s.socketType == 'tcp') - .toList(); - expect(tcpSockets, isEmpty); - }); - - test('enabling does not hide websocket sockets', () { - // Inject a websocket socket via the public processNetworkTraffic API - // to verify it is preserved when the filter is active. - controller.processNetworkTraffic( - sockets: [ - SocketStatistic.parse({ - 'id': 'ws-1', - 'startTime': DateTime(2021).microsecondsSinceEpoch, - 'lastReadTime': 25, - 'lastWriteTime': 30, - 'address': '1.2.3.4', - 'port': 443, - 'socketType': 'websocket', - 'readBytes': 10, - 'writeBytes': 10, - })!, - ], - httpRequests: [], - ); - - // Now enable the filter — tcp sockets should be hidden, websocket kept. - controller.filterHttpSockets.value = true; - - final webSockets = controller.filteredData.value - .whereType() - .where((s) => s.socketType == 'websocket') - .toList(); - expect(webSockets, hasLength(1)); - - final tcpSockets = controller.filteredData.value - .whereType() - .where((s) => s.socketType == 'tcp') - .toList(); - expect(tcpSockets, isEmpty); - }); - test('disabling restores tcp sockets in filteredData', () { - // Enable and verify sockets are hidden. - controller.filterHttpSockets.value = true; - expect( - controller.filteredData.value, - hasLength(numRequestsWithoutTcpSockets), - ); - - // Disable and verify sockets are restored. - controller.filterHttpSockets.value = false; - expect(controller.filteredData.value, hasLength(numRequests)); + expect(controller.filteredData.value, hasLength(numRequests)); - final tcpSockets = controller.filteredData.value - .whereType() - .where((s) => s.socketType == 'tcp') - .toList(); - expect(tcpSockets, hasLength(numTcpSockets)); - }); + // Enable the hide HTTP sockets toggle filter. + final socketFilter = controller.settingFilters + .whereType>() + .firstWhere((f) => f.id == NetworkController.hideHttpSocketsFilterId); + + // Pass the updated setting filters through setActiveFilter so the + // base class FilterControllerMixin picks them up and re-filters. + socketFilter.setting.value = true; + controller.setActiveFilter( + settingFilters: controller.settingFilters + .whereType>() + .toList(), + ); - test('raw requests list is never modified by the toggle', () { - // Enabling the filter must never mutate the underlying requests list — - // only filteredData should change. - controller.filterHttpSockets.value = true; - expect(controller.requests.value, hasLength(numRequests)); + expect(controller.filteredData.value, hasLength(numRequests - numTcpSockets)); - controller.filterHttpSockets.value = false; - expect(controller.requests.value, hasLength(numRequests)); - }); + final tcpSockets = controller.filteredData.value + .whereType() + .where((s) => s.socketType == 'tcp') + .toList(); + expect(tcpSockets, isEmpty); - test('composes correctly with query filter', () { - // With socket filter on AND a query filter that matches HTTP requests - // only, tcp sockets should be excluded by the socket filter before - // the query filter runs. - controller.filterHttpSockets.value = true; - controller.setActiveFilter(query: 'jsonplaceholder'); - - // 5 HTTP requests match 'jsonplaceholder'; 0 tcp sockets should appear. - expect(controller.filteredData.value, hasLength(5)); - final tcpSockets = controller.filteredData.value - .whereType() - .where((s) => s.socketType == 'tcp') - .toList(); - expect(tcpSockets, isEmpty); - - // Reset query filter but keep socket filter on. - controller.setActiveFilter(); - expect( - controller.filteredData.value, - hasLength(numRequestsWithoutTcpSockets), - ); - }); + // Disable and verify sockets are restored. + socketFilter.setting.value = false; + controller.setActiveFilter( + settingFilters: controller.settingFilters + .whereType>() + .toList(), + ); + expect(controller.filteredData.value, hasLength(numRequests)); }); }); From d5213a8fe5f9ec88cd556909f55b5bb37701beac Mon Sep 17 00:00:00 2001 From: rishika0212 Date: Thu, 12 Mar 2026 23:54:57 +0530 Subject: [PATCH 3/3] Removed visibleForTesting annotation and fixed analyzer warnings --- .../lib/src/screens/network/network_controller.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/devtools_app/lib/src/screens/network/network_controller.dart b/packages/devtools_app/lib/src/screens/network/network_controller.dart index c60527c9c93..89d6eaa020e 100644 --- a/packages/devtools_app/lib/src/screens/network/network_controller.dart +++ b/packages/devtools_app/lib/src/screens/network/network_controller.dart @@ -180,7 +180,6 @@ class NetworkController extends DevToolsScreenController PeriodicDebouncer? _pollingTimer; - // Removed @visibleForTesting annotation bool get isPolling => _pollingTimer != null; static const _pollingDuration = Duration(milliseconds: 2000); @@ -250,7 +249,6 @@ class NetworkController extends DevToolsScreenController } } - // Removed @visibleForTesting annotation void processNetworkTrafficHelper( List sockets, List? httpRequests,