Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
34d2c8d0
Unverified
Commit
34d2c8d0
authored
Apr 04, 2023
by
Victoria Ashworth
Committed by
GitHub
Apr 04, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Better support for wireless devices in IDEs (#123716)
Better support for wireless devices in IDEs
parent
e3bc8efd
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
394 additions
and
176 deletions
+394
-176
devices.dart
packages/flutter_tools/lib/src/ios/devices.dart
+99
-21
xcdevice.dart
packages/flutter_tools/lib/src/macos/xcdevice.dart
+151
-123
devices_test.dart
...es/flutter_tools/test/general.shard/ios/devices_test.dart
+82
-17
xcode_test.dart
...es/flutter_tools/test/general.shard/macos/xcode_test.dart
+62
-15
No files found.
packages/flutter_tools/lib/src/ios/devices.dart
View file @
34d2c8d0
...
@@ -59,7 +59,31 @@ class IOSDevices extends PollingDeviceDiscovery {
...
@@ -59,7 +59,31 @@ class IOSDevices extends PollingDeviceDiscovery {
@override
@override
bool
get
requiresExtendedWirelessDeviceDiscovery
=>
true
;
bool
get
requiresExtendedWirelessDeviceDiscovery
=>
true
;
StreamSubscription
<
Map
<
XCDeviceEvent
,
String
>>?
_observedDeviceEventsSubscription
;
StreamSubscription
<
XCDeviceEventNotification
>?
_observedDeviceEventsSubscription
;
/// Cache for all devices found by `xcdevice list`, including not connected
/// devices. Used to minimize the need to call `xcdevice list`.
///
/// Separate from `deviceNotifier` since `deviceNotifier` should only contain
/// connected devices.
final
Map
<
String
,
IOSDevice
>
_cachedPolledDevices
=
<
String
,
IOSDevice
>{};
/// Maps device id to a map of the device's observed connections. When the
/// mapped connection is `true`, that means that observed events indicated
/// the device is connected via that particular interface.
///
/// The device id must be missing from the map or both interfaces must be
/// false for the device to be considered disconnected.
///
/// Example:
/// {
/// device-id: {
/// usb: false,
/// wifi: false,
/// },
/// }
final
Map
<
String
,
Map
<
XCDeviceEventInterface
,
bool
>>
_observedConnectionsByDeviceId
=
<
String
,
Map
<
XCDeviceEventInterface
,
bool
>>{};
@override
@override
Future
<
void
>
startPolling
()
async
{
Future
<
void
>
startPolling
()
async
{
...
@@ -75,16 +99,13 @@ class IOSDevices extends PollingDeviceDiscovery {
...
@@ -75,16 +99,13 @@ class IOSDevices extends PollingDeviceDiscovery {
deviceNotifier
??=
ItemListNotifier
<
Device
>();
deviceNotifier
??=
ItemListNotifier
<
Device
>();
// Start by populating all currently attached devices.
// Start by populating all currently attached devices.
final
List
<
Device
>
devices
=
await
pollingGetDevices
();
_updateCachedDevices
(
await
pollingGetDevices
());
_updateNotifierFromCache
();
// Only show connected devices.
final
List
<
Device
>
filteredDevices
=
devices
.
where
((
Device
device
)
=>
device
.
isConnected
==
true
).
toList
();
deviceNotifier
!.
updateWithNewList
(
filteredDevices
);
// cancel any outstanding subscriptions.
// cancel any outstanding subscriptions.
await
_observedDeviceEventsSubscription
?.
cancel
();
await
_observedDeviceEventsSubscription
?.
cancel
();
_observedDeviceEventsSubscription
=
xcdevice
.
observedDeviceEvents
()?.
listen
(
_observedDeviceEventsSubscription
=
xcdevice
.
observedDeviceEvents
()?.
listen
(
_
onDeviceEvent
,
onDeviceEvent
,
onError:
(
Object
error
,
StackTrace
stack
)
{
onError:
(
Object
error
,
StackTrace
stack
)
{
_logger
.
printTrace
(
'Process exception running xcdevice observe:
\n
$error
\n
$stack
'
);
_logger
.
printTrace
(
'Process exception running xcdevice observe:
\n
$error
\n
$stack
'
);
},
onDone:
()
{
},
onDone:
()
{
...
@@ -98,32 +119,89 @@ class IOSDevices extends PollingDeviceDiscovery {
...
@@ -98,32 +119,89 @@ class IOSDevices extends PollingDeviceDiscovery {
);
);
}
}
Future
<
void
>
_onDeviceEvent
(
Map
<
XCDeviceEvent
,
String
>
event
)
async
{
@visibleForTesting
final
XCDeviceEvent
eventType
=
event
.
containsKey
(
XCDeviceEvent
.
attach
)
?
XCDeviceEvent
.
attach
:
XCDeviceEvent
.
detach
;
Future
<
void
>
onDeviceEvent
(
XCDeviceEventNotification
event
)
async
{
final
String
?
deviceIdentifier
=
event
[
eventType
];
final
ItemListNotifier
<
Device
>?
notifier
=
deviceNotifier
;
final
ItemListNotifier
<
Device
>?
notifier
=
deviceNotifier
;
if
(
notifier
==
null
)
{
if
(
notifier
==
null
)
{
return
;
return
;
}
}
Device
?
knownDevice
;
Device
?
knownDevice
;
for
(
final
Device
device
in
notifier
.
items
)
{
for
(
final
Device
device
in
notifier
.
items
)
{
if
(
device
.
id
==
deviceIdentifier
)
{
if
(
device
.
id
==
event
.
deviceIdentifier
)
{
knownDevice
=
device
;
knownDevice
=
device
;
}
}
}
}
// Ignore already discovered devices (maybe populated at the beginning).
final
Map
<
XCDeviceEventInterface
,
bool
>
deviceObservedConnections
=
if
(
eventType
==
XCDeviceEvent
.
attach
&&
knownDevice
==
null
)
{
_observedConnectionsByDeviceId
[
event
.
deviceIdentifier
]
??
// There's no way to get details for an individual attached device,
<
XCDeviceEventInterface
,
bool
>{
// so repopulate them all.
XCDeviceEventInterface
.
usb
:
false
,
final
List
<
Device
>
devices
=
await
pollingGetDevices
();
XCDeviceEventInterface
.
wifi
:
false
,
};
if
(
event
.
eventType
==
XCDeviceEvent
.
attach
)
{
// Update device's observed connections.
deviceObservedConnections
[
event
.
eventInterface
]
=
true
;
_observedConnectionsByDeviceId
[
event
.
deviceIdentifier
]
=
deviceObservedConnections
;
// If device was not already in notifier, add it.
if
(
knownDevice
==
null
)
{
if
(
_cachedPolledDevices
[
event
.
deviceIdentifier
]
==
null
)
{
// If device is not found in cache, there's no way to get details
// for an individual attached device, so repopulate them all.
_updateCachedDevices
(
await
pollingGetDevices
());
}
_updateNotifierFromCache
();
}
}
else
{
// Update device's observed connections.
deviceObservedConnections
[
event
.
eventInterface
]
=
false
;
_observedConnectionsByDeviceId
[
event
.
deviceIdentifier
]
=
deviceObservedConnections
;
// If device is in the notifier and does not have other observed
// connections, remove it.
if
(
knownDevice
!=
null
&&
!
_deviceHasObservedConnection
(
deviceObservedConnections
))
{
notifier
.
removeItem
(
knownDevice
);
}
}
}
/// Adds or updates devices in cache. Does not remove devices from cache.
void
_updateCachedDevices
(
List
<
Device
>
devices
)
{
for
(
final
Device
device
in
devices
)
{
if
(
device
is
!
IOSDevice
)
{
continue
;
}
_cachedPolledDevices
[
device
.
id
]
=
device
;
}
}
// Only show connected devices.
/// Updates notifier with devices found in the cache that are determined
final
List
<
Device
>
filteredDevices
=
devices
.
where
((
Device
device
)
=>
device
.
isConnected
==
true
).
toList
();
/// to be connected.
notifier
.
updateWithNewList
(
filteredDevices
);
void
_updateNotifierFromCache
()
{
}
else
if
(
eventType
==
XCDeviceEvent
.
detach
&&
knownDevice
!=
null
)
{
final
ItemListNotifier
<
Device
>?
notifier
=
deviceNotifier
;
notifier
.
removeItem
(
knownDevice
);
if
(
notifier
==
null
)
{
return
;
}
}
// Device is connected if it has either an observed usb or wifi connection
// or it has not been observed but was found as connected in the cache.
final
List
<
Device
>
connectedDevices
=
_cachedPolledDevices
.
values
.
where
((
Device
device
)
{
final
Map
<
XCDeviceEventInterface
,
bool
>?
deviceObservedConnections
=
_observedConnectionsByDeviceId
[
device
.
id
];
return
(
deviceObservedConnections
!=
null
&&
_deviceHasObservedConnection
(
deviceObservedConnections
))
||
(
deviceObservedConnections
==
null
&&
device
.
isConnected
);
}).
toList
();
notifier
.
updateWithNewList
(
connectedDevices
);
}
bool
_deviceHasObservedConnection
(
Map
<
XCDeviceEventInterface
,
bool
>
deviceObservedConnections
,
)
{
return
(
deviceObservedConnections
[
XCDeviceEventInterface
.
usb
]
??
false
)
||
(
deviceObservedConnections
[
XCDeviceEventInterface
.
wifi
]
??
false
);
}
}
@override
@override
...
...
packages/flutter_tools/lib/src/macos/xcdevice.dart
View file @
34d2c8d0
...
@@ -86,7 +86,8 @@ class XCDevice {
...
@@ -86,7 +86,8 @@ class XCDevice {
}
}
void
dispose
()
{
void
dispose
()
{
_deviceObservationProcess
?.
kill
();
_usbDeviceObserveProcess
?.
kill
();
_wifiDeviceObserveProcess
?.
kill
();
_usbDeviceWaitProcess
?.
kill
();
_usbDeviceWaitProcess
?.
kill
();
_wifiDeviceWaitProcess
?.
kill
();
_wifiDeviceWaitProcess
?.
kill
();
}
}
...
@@ -99,8 +100,10 @@ class XCDevice {
...
@@ -99,8 +100,10 @@ class XCDevice {
final
IProxy
_iProxy
;
final
IProxy
_iProxy
;
List
<
Object
>?
_cachedListResults
;
List
<
Object
>?
_cachedListResults
;
Process
?
_deviceObservationProcess
;
StreamController
<
Map
<
XCDeviceEvent
,
String
>>?
_deviceIdentifierByEvent
;
Process
?
_usbDeviceObserveProcess
;
Process
?
_wifiDeviceObserveProcess
;
StreamController
<
XCDeviceEventNotification
>?
_observeStreamController
;
@visibleForTesting
@visibleForTesting
StreamController
<
XCDeviceEventNotification
>?
waitStreamController
;
StreamController
<
XCDeviceEventNotification
>?
waitStreamController
;
...
@@ -109,9 +112,9 @@ class XCDevice {
...
@@ -109,9 +112,9 @@ class XCDevice {
Process
?
_wifiDeviceWaitProcess
;
Process
?
_wifiDeviceWaitProcess
;
void
_setupDeviceIdentifierByEventStream
()
{
void
_setupDeviceIdentifierByEventStream
()
{
// _
deviceIdentifierByEvent
Should always be available for listeners
// _
observeStreamController
Should always be available for listeners
// in case polling needs to be stopped and restarted.
// in case polling needs to be stopped and restarted.
_
deviceIdentifierByEvent
=
StreamController
<
Map
<
XCDeviceEvent
,
String
>
>.
broadcast
(
_
observeStreamController
=
StreamController
<
XCDeviceEventNotification
>.
broadcast
(
onListen:
_startObservingTetheredIOSDevices
,
onListen:
_startObservingTetheredIOSDevices
,
onCancel:
_stopObservingTetheredIOSDevices
,
onCancel:
_stopObservingTetheredIOSDevices
,
);
);
...
@@ -121,7 +124,7 @@ class XCDevice {
...
@@ -121,7 +124,7 @@ class XCDevice {
Future
<
List
<
Object
>?>
_getAllDevices
({
Future
<
List
<
Object
>?>
_getAllDevices
({
bool
useCache
=
false
,
bool
useCache
=
false
,
required
Duration
timeout
required
Duration
timeout
,
})
async
{
})
async
{
if
(!
isInstalled
)
{
if
(!
isInstalled
)
{
_logger
.
printTrace
(
"Xcode not found. Run 'flutter doctor' for more information."
);
_logger
.
printTrace
(
"Xcode not found. Run 'flutter doctor' for more information."
);
...
@@ -166,14 +169,14 @@ class XCDevice {
...
@@ -166,14 +169,14 @@ class XCDevice {
/// Observe identifiers (UDIDs) of devices as they attach and detach.
/// Observe identifiers (UDIDs) of devices as they attach and detach.
///
///
/// Each attach and detach event
is a tuple of one event type
/// Each attach and detach event
contains information on the event type,
///
and identifi
er.
///
the event interface, and the device identif
er.
Stream
<
Map
<
XCDeviceEvent
,
String
>
>?
observedDeviceEvents
()
{
Stream
<
XCDeviceEventNotification
>?
observedDeviceEvents
()
{
if
(!
isInstalled
)
{
if
(!
isInstalled
)
{
_logger
.
printTrace
(
"Xcode not found. Run 'flutter doctor' for more information."
);
_logger
.
printTrace
(
"Xcode not found. Run 'flutter doctor' for more information."
);
return
null
;
return
null
;
}
}
return
_
deviceIdentifierByEvent
?.
stream
;
return
_
observeStreamController
?.
stream
;
}
}
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
...
@@ -183,66 +186,130 @@ class XCDevice {
...
@@ -183,66 +186,130 @@ class XCDevice {
Future
<
void
>
_startObservingTetheredIOSDevices
()
async
{
Future
<
void
>
_startObservingTetheredIOSDevices
()
async
{
try
{
try
{
if
(
_
deviceObservation
Process
!=
null
)
{
if
(
_
usbDeviceObserveProcess
!=
null
||
_wifiDeviceObserve
Process
!=
null
)
{
throw
Exception
(
'xcdevice observe restart failed'
);
throw
Exception
(
'xcdevice observe restart failed'
);
}
}
// Run in interactive mode (via script) to convince
_usbDeviceObserveProcess
=
await
_startObserveProcess
(
// xcdevice it has a terminal attached in order to redirect stdout.
XCDeviceEventInterface
.
usb
,
_deviceObservationProcess
=
await
_processUtils
.
start
(
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
...
_xcode
.
xcrunCommand
(),
'xcdevice'
,
'observe'
,
'--both'
,
],
);
);
final
StreamSubscription
<
String
>
stdoutSubscription
=
_deviceObservationProcess
!.
stdout
_wifiDeviceObserveProcess
=
await
_startObserveProcess
(
.
transform
<
String
>(
utf8
.
decoder
)
XCDeviceEventInterface
.
wifi
,
.
transform
<
String
>(
const
LineSplitter
())
);
.
listen
((
String
line
)
{
final
XCDeviceEventNotification
?
event
=
_processXCDeviceStdOut
(
final
Future
<
void
>
usbProcessExited
=
_usbDeviceObserveProcess
!.
exitCode
.
then
((
int
status
)
{
line
,
_logger
.
printTrace
(
'xcdevice observe --usb exited with code
$exitCode
'
);
XCDeviceEventInterface
.
usb
,
// Kill other process in case only one was killed.
);
_wifiDeviceObserveProcess
?.
kill
();
if
(
event
!=
null
)
{
_deviceIdentifierByEvent
?.
add
(<
XCDeviceEvent
,
String
>{
event
.
eventType
:
event
.
deviceIdentifier
,
});
}
});
});
final
StreamSubscription
<
String
>
stderrSubscription
=
_deviceObservationProcess
!.
stderr
.
transform
<
String
>(
utf8
.
decoder
)
final
Future
<
void
>
wifiProcessExited
=
_wifiDeviceObserveProcess
!.
exitCode
.
then
((
int
status
)
{
.
transform
<
String
>(
const
LineSplitter
())
_logger
.
printTrace
(
'xcdevice observe --wifi exited with code
$exitCode
'
);
.
listen
((
String
line
)
{
// Kill other process in case only one was killed.
_
logger
.
printTrace
(
'xcdevice observe error:
$line
'
);
_
usbDeviceObserveProcess
?.
kill
(
);
});
});
unawaited
(
_deviceObservationProcess
?.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice exited with code
$exitCode
'
);
unawaited
(
Future
.
wait
(<
Future
<
void
>>[
u
nawaited
(
stdoutSubscription
.
cancel
());
u
sbProcessExited
,
unawaited
(
stderrSubscription
.
cancel
());
wifiProcessExited
,
}
).
whenComplete
(()
async
{
]
).
whenComplete
(()
async
{
if
(
_
deviceIdentifierByEvent
?.
hasListener
??
false
)
{
if
(
_
observeStreamController
?.
hasListener
??
false
)
{
// Tell listeners the process died.
// Tell listeners the process died.
await
_
deviceIdentifierByEvent
?.
close
();
await
_
observeStreamController
?.
close
();
}
}
_deviceObservationProcess
=
null
;
_usbDeviceObserveProcess
=
null
;
_wifiDeviceObserveProcess
=
null
;
// Reopen it so new listeners can resume polling.
// Reopen it so new listeners can resume polling.
_setupDeviceIdentifierByEventStream
();
_setupDeviceIdentifierByEventStream
();
}));
}));
}
on
ProcessException
catch
(
exception
,
stackTrace
)
{
}
on
ProcessException
catch
(
exception
,
stackTrace
)
{
_
deviceIdentifierByEvent
?.
addError
(
exception
,
stackTrace
);
_
observeStreamController
?.
addError
(
exception
,
stackTrace
);
}
on
ArgumentError
catch
(
exception
,
stackTrace
)
{
}
on
ArgumentError
catch
(
exception
,
stackTrace
)
{
_
deviceIdentifierByEvent
?.
addError
(
exception
,
stackTrace
);
_
observeStreamController
?.
addError
(
exception
,
stackTrace
);
}
}
}
}
Future
<
Process
>
_startObserveProcess
(
XCDeviceEventInterface
eventInterface
)
{
// Run in interactive mode (via script) to convince
// xcdevice it has a terminal attached in order to redirect stdout.
return
_streamXCDeviceEventCommand
(
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
...
_xcode
.
xcrunCommand
(),
'xcdevice'
,
'observe'
,
'--
${eventInterface.name}
'
,
],
prefix:
'xcdevice observe --
${eventInterface.name}
: '
,
mapFunction:
(
String
line
)
{
final
XCDeviceEventNotification
?
event
=
_processXCDeviceStdOut
(
line
,
eventInterface
,
);
if
(
event
!=
null
)
{
_observeStreamController
?.
add
(
event
);
}
return
line
;
},
);
}
/// Starts the command and streams stdout/stderr from the child process to
/// this process' stdout/stderr.
///
/// If [mapFunction] is present, all lines are forwarded to [mapFunction] for
/// further processing.
Future
<
Process
>
_streamXCDeviceEventCommand
(
List
<
String
>
cmd
,
{
String
prefix
=
''
,
StringConverter
?
mapFunction
,
})
async
{
final
Process
process
=
await
_processUtils
.
start
(
cmd
);
final
StreamSubscription
<
String
>
stdoutSubscription
=
process
.
stdout
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
String
?
mappedLine
=
line
;
if
(
mapFunction
!=
null
)
{
mappedLine
=
mapFunction
(
line
);
}
if
(
mappedLine
!=
null
)
{
final
String
message
=
'
$prefix$mappedLine
'
;
_logger
.
printTrace
(
message
);
}
});
final
StreamSubscription
<
String
>
stderrSubscription
=
process
.
stderr
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
String
?
mappedLine
=
line
;
if
(
mapFunction
!=
null
)
{
mappedLine
=
mapFunction
(
line
);
}
if
(
mappedLine
!=
null
)
{
_logger
.
printError
(
'
$prefix$mappedLine
'
,
wrap:
false
);
}
});
unawaited
(
process
.
exitCode
.
whenComplete
(()
{
stdoutSubscription
.
cancel
();
stderrSubscription
.
cancel
();
}));
return
process
;
}
void
_stopObservingTetheredIOSDevices
()
{
_usbDeviceObserveProcess
?.
kill
();
_wifiDeviceObserveProcess
?.
kill
();
}
XCDeviceEventNotification
?
_processXCDeviceStdOut
(
XCDeviceEventNotification
?
_processXCDeviceStdOut
(
String
line
,
String
line
,
XCDeviceEventInterface
eventInterface
,
XCDeviceEventInterface
eventInterface
,
...
@@ -275,10 +342,6 @@ class XCDevice {
...
@@ -275,10 +342,6 @@ class XCDevice {
return
null
;
return
null
;
}
}
void
_stopObservingTetheredIOSDevices
()
{
_deviceObservationProcess
?.
kill
();
}
/// Wait for a connect event for a specific device. Must use device's exact UDID.
/// Wait for a connect event for a specific device. Must use device's exact UDID.
///
///
/// To cancel this process, call [cancelWaitForDeviceToConnect].
/// To cancel this process, call [cancelWaitForDeviceToConnect].
...
@@ -292,72 +355,26 @@ class XCDevice {
...
@@ -292,72 +355,26 @@ class XCDevice {
waitStreamController
=
StreamController
<
XCDeviceEventNotification
>();
waitStreamController
=
StreamController
<
XCDeviceEventNotification
>();
// Run in interactive mode (via script) to convince
_usbDeviceWaitProcess
=
await
_startWaitProcess
(
// xcdevice it has a terminal attached in order to redirect stdout.
deviceId
,
_usbDeviceWaitProcess
=
await
_processUtils
.
start
(
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
...
_xcode
.
xcrunCommand
(),
'xcdevice'
,
'wait'
,
'--
${XCDeviceEventInterface.usb.name}
'
,
deviceId
,
],
);
_wifiDeviceWaitProcess
=
await
_processUtils
.
start
(
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
...
_xcode
.
xcrunCommand
(),
'xcdevice'
,
'wait'
,
'--
${XCDeviceEventInterface.wifi.name}
'
,
deviceId
,
],
);
final
StreamSubscription
<
String
>
usbStdoutSubscription
=
_processWaitStdOut
(
_usbDeviceWaitProcess
!,
XCDeviceEventInterface
.
usb
,
XCDeviceEventInterface
.
usb
,
);
);
final
StreamSubscription
<
String
>
wifiStdoutSubscription
=
_processWaitStdOut
(
_wifiDeviceWaitProcess
!,
_wifiDeviceWaitProcess
=
await
_startWaitProcess
(
deviceId
,
XCDeviceEventInterface
.
wifi
,
XCDeviceEventInterface
.
wifi
,
);
);
final
StreamSubscription
<
String
>
usbStderrSubscription
=
_usbDeviceWaitProcess
!.
stderr
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
_logger
.
printTrace
(
'xcdevice wait --usb error:
$line
'
);
});
final
StreamSubscription
<
String
>
wifiStderrSubscription
=
_wifiDeviceWaitProcess
!.
stderr
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
_logger
.
printTrace
(
'xcdevice wait --wifi error:
$line
'
);
});
final
Future
<
void
>
usbProcessExited
=
_usbDeviceWaitProcess
!.
exitCode
.
then
((
int
status
)
{
final
Future
<
void
>
usbProcessExited
=
_usbDeviceWaitProcess
!.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice wait --usb exited with code
$exitCode
'
);
_logger
.
printTrace
(
'xcdevice wait --usb exited with code
$exitCode
'
);
// Kill other process in case only one was killed.
// Kill other process in case only one was killed.
_wifiDeviceWaitProcess
?.
kill
();
_wifiDeviceWaitProcess
?.
kill
();
unawaited
(
usbStdoutSubscription
.
cancel
());
unawaited
(
usbStderrSubscription
.
cancel
());
});
});
final
Future
<
void
>
wifiProcessExited
=
_wifiDeviceWaitProcess
!.
exitCode
.
then
((
int
status
)
{
final
Future
<
void
>
wifiProcessExited
=
_wifiDeviceWaitProcess
!.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice wait --wifi exited with code
$exitCode
'
);
_logger
.
printTrace
(
'xcdevice wait --wifi exited with code
$exitCode
'
);
// Kill other process in case only one was killed.
// Kill other process in case only one was killed.
_usbDeviceWaitProcess
?.
kill
();
_usbDeviceWaitProcess
?.
kill
();
unawaited
(
wifiStdoutSubscription
.
cancel
());
unawaited
(
wifiStderrSubscription
.
cancel
());
});
});
final
Future
<
void
>
allProcessesExited
=
Future
.
wait
(
final
Future
<
void
>
allProcessesExited
=
Future
.
wait
(
...
@@ -389,22 +406,33 @@ class XCDevice {
...
@@ -389,22 +406,33 @@ class XCDevice {
return
null
;
return
null
;
}
}
StreamSubscription
<
String
>
_processWaitStdOut
(
Future
<
Process
>
_startWaitProcess
(
String
deviceId
,
XCDeviceEventInterface
eventInterface
)
{
Process
process
,
// Run in interactive mode (via script) to convince
XCDeviceEventInterface
eventInterface
,
// xcdevice it has a terminal attached in order to redirect stdout.
)
{
return
_streamXCDeviceEventCommand
(
return
process
.
stdout
<
String
>[
.
transform
<
String
>(
utf8
.
decoder
)
'script'
,
.
transform
<
String
>(
const
LineSplitter
())
'-t'
,
.
listen
((
String
line
)
{
'0'
,
final
XCDeviceEventNotification
?
event
=
_processXCDeviceStdOut
(
'/dev/null'
,
line
,
...
_xcode
.
xcrunCommand
(),
eventInterface
,
'xcdevice'
,
);
'wait'
,
if
(
event
!=
null
&&
event
.
eventType
==
XCDeviceEvent
.
attach
)
{
'--
${eventInterface.name}
'
,
waitStreamController
?.
add
(
event
);
deviceId
,
}
],
});
prefix:
'xcdevice wait --
${eventInterface.name}
: '
,
mapFunction:
(
String
line
)
{
final
XCDeviceEventNotification
?
event
=
_processXCDeviceStdOut
(
line
,
eventInterface
,
);
if
(
event
!=
null
&&
event
.
eventType
==
XCDeviceEvent
.
attach
)
{
waitStreamController
?.
add
(
event
);
}
return
line
;
},
);
}
}
void
cancelWaitForDeviceToConnect
()
{
void
cancelWaitForDeviceToConnect
()
{
...
...
packages/flutter_tools/test/general.shard/ios/devices_test.dart
View file @
34d2c8d0
...
@@ -413,7 +413,7 @@ void main() {
...
@@ -413,7 +413,7 @@ void main() {
});
});
testWithoutContext
(
'start polling'
,
()
async
{
testWithoutContext
(
'start polling'
,
()
async
{
final
IOSDevices
iosDevices
=
IOSDevices
(
final
TestIOSDevices
iosDevices
=
Test
IOSDevices
(
platform:
macPlatform
,
platform:
macPlatform
,
xcdevice:
xcdevice
,
xcdevice:
xcdevice
,
iosWorkflow:
iosWorkflow
,
iosWorkflow:
iosWorkflow
,
...
@@ -447,25 +447,68 @@ void main() {
...
@@ -447,25 +447,68 @@ void main() {
expect
(
iosDevices
.
deviceNotifier
!.
items
,
isEmpty
);
expect
(
iosDevices
.
deviceNotifier
!.
items
,
isEmpty
);
expect
(
xcdevice
.
deviceEventController
.
hasListener
,
isTrue
);
expect
(
xcdevice
.
deviceEventController
.
hasListener
,
isTrue
);
xcdevice
.
deviceEventController
.
add
(<
XCDeviceEvent
,
String
>{
xcdevice
.
deviceEventController
.
add
(
XCDeviceEvent
.
attach
:
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
,
XCDeviceEventNotification
(
});
XCDeviceEvent
.
attach
,
XCDeviceEventInterface
.
usb
,
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
),
);
await
added
.
future
;
await
added
.
future
;
expect
(
iosDevices
.
deviceNotifier
!.
items
.
length
,
2
);
expect
(
iosDevices
.
deviceNotifier
!.
items
.
length
,
2
);
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device1
));
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device1
));
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device2
));
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device2
));
expect
(
iosDevices
.
eventsReceived
,
1
);
xcdevice
.
deviceEventController
.
add
(<
XCDeviceEvent
,
String
>{
XCDeviceEvent
.
detach
:
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
,
iosDevices
.
resetEventCompleter
();
});
xcdevice
.
deviceEventController
.
add
(
XCDeviceEventNotification
(
XCDeviceEvent
.
attach
,
XCDeviceEventInterface
.
wifi
,
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
),
);
await
iosDevices
.
receivedEvent
.
future
;
expect
(
iosDevices
.
deviceNotifier
!.
items
.
length
,
2
);
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device1
));
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device2
));
expect
(
iosDevices
.
eventsReceived
,
2
);
iosDevices
.
resetEventCompleter
();
xcdevice
.
deviceEventController
.
add
(
XCDeviceEventNotification
(
XCDeviceEvent
.
detach
,
XCDeviceEventInterface
.
usb
,
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
),
);
await
iosDevices
.
receivedEvent
.
future
;
expect
(
iosDevices
.
deviceNotifier
!.
items
.
length
,
2
);
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device1
));
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device2
));
expect
(
iosDevices
.
eventsReceived
,
3
);
xcdevice
.
deviceEventController
.
add
(
XCDeviceEventNotification
(
XCDeviceEvent
.
detach
,
XCDeviceEventInterface
.
wifi
,
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
),
);
await
removed
.
future
;
await
removed
.
future
;
expect
(
iosDevices
.
deviceNotifier
!.
items
,
<
Device
>[
device2
]);
expect
(
iosDevices
.
deviceNotifier
!.
items
,
<
Device
>[
device2
]);
expect
(
iosDevices
.
eventsReceived
,
4
);
// Remove stream will throw over-completion if called more than once
// which proves this is ignored.
iosDevices
.
resetEventCompleter
();
xcdevice
.
deviceEventController
.
add
(<
XCDeviceEvent
,
String
>{
xcdevice
.
deviceEventController
.
add
(
XCDeviceEvent
.
detach
:
'bogus'
,
XCDeviceEventNotification
(
});
XCDeviceEvent
.
detach
,
XCDeviceEventInterface
.
usb
,
'bogus'
),
);
await
iosDevices
.
receivedEvent
.
future
;
expect
(
iosDevices
.
eventsReceived
,
5
);
expect
(
addedCount
,
2
);
expect
(
addedCount
,
2
);
...
@@ -485,7 +528,7 @@ void main() {
...
@@ -485,7 +528,7 @@ void main() {
xcdevice
.
devices
.
add
(<
IOSDevice
>[]);
xcdevice
.
devices
.
add
(<
IOSDevice
>[]);
xcdevice
.
devices
.
add
(<
IOSDevice
>[]);
xcdevice
.
devices
.
add
(<
IOSDevice
>[]);
final
StreamController
<
Map
<
XCDeviceEvent
,
String
>>
rescheduledStream
=
StreamController
<
Map
<
XCDeviceEvent
,
String
>
>();
final
StreamController
<
XCDeviceEventNotification
>
rescheduledStream
=
StreamController
<
XCDeviceEventNotification
>();
unawaited
(
xcdevice
.
deviceEventController
.
done
.
whenComplete
(()
{
unawaited
(
xcdevice
.
deviceEventController
.
done
.
whenComplete
(()
{
xcdevice
.
deviceEventController
=
rescheduledStream
;
xcdevice
.
deviceEventController
=
rescheduledStream
;
...
@@ -723,13 +766,35 @@ class FakeIOSApp extends Fake implements IOSApp {
...
@@ -723,13 +766,35 @@ class FakeIOSApp extends Fake implements IOSApp {
final
String
name
;
final
String
name
;
}
}
class
TestIOSDevices
extends
IOSDevices
{
TestIOSDevices
({
required
super
.
platform
,
required
super
.
xcdevice
,
required
super
.
iosWorkflow
,
required
super
.
logger
,});
Completer
<
void
>
receivedEvent
=
Completer
<
void
>();
int
eventsReceived
=
0
;
void
resetEventCompleter
()
{
receivedEvent
=
Completer
<
void
>();
}
@override
Future
<
void
>
onDeviceEvent
(
XCDeviceEventNotification
event
)
async
{
await
super
.
onDeviceEvent
(
event
);
if
(!
receivedEvent
.
isCompleted
)
{
receivedEvent
.
complete
();
}
eventsReceived
++;
return
;
}
}
class
FakeIOSWorkflow
extends
Fake
implements
IOSWorkflow
{
}
class
FakeIOSWorkflow
extends
Fake
implements
IOSWorkflow
{
}
class
FakeXcdevice
extends
Fake
implements
XCDevice
{
class
FakeXcdevice
extends
Fake
implements
XCDevice
{
int
getAvailableIOSDevicesCount
=
0
;
int
getAvailableIOSDevicesCount
=
0
;
final
List
<
List
<
IOSDevice
>>
devices
=
<
List
<
IOSDevice
>>[];
final
List
<
List
<
IOSDevice
>>
devices
=
<
List
<
IOSDevice
>>[];
final
List
<
String
>
diagnostics
=
<
String
>[];
final
List
<
String
>
diagnostics
=
<
String
>[];
StreamController
<
Map
<
XCDeviceEvent
,
String
>>
deviceEventController
=
StreamController
<
Map
<
XCDeviceEvent
,
String
>>();
StreamController
<
XCDeviceEventNotification
>
deviceEventController
=
StreamController
<
XCDeviceEventNotification
>();
XCDeviceEventNotification
?
waitForDeviceEvent
;
XCDeviceEventNotification
?
waitForDeviceEvent
;
@override
@override
...
@@ -741,7 +806,7 @@ class FakeXcdevice extends Fake implements XCDevice {
...
@@ -741,7 +806,7 @@ class FakeXcdevice extends Fake implements XCDevice {
}
}
@override
@override
Stream
<
Map
<
XCDeviceEvent
,
String
>
>
observedDeviceEvents
()
{
Stream
<
XCDeviceEventNotification
>
observedDeviceEvents
()
{
return
deviceEventController
.
stream
;
return
deviceEventController
.
stream
;
}
}
...
...
packages/flutter_tools/test/general.shard/macos/xcode_test.dart
View file @
34d2c8d0
...
@@ -342,32 +342,57 @@ void main() {
...
@@ -342,32 +342,57 @@ void main() {
'xcrun'
,
'xcrun'
,
'xcdevice'
,
'xcdevice'
,
'observe'
,
'observe'
,
'--both'
,
'--usb'
,
],
stdout:
'Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
\n
'
],
stdout:
'Listening for all devices, on USB.
\n
'
'Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
\n
'
'Attach: 00008027-00192736010F802E
\n
'
'Attach: 00008027-00192736010F802E
\n
'
'Detach: d83d5bc53967baa0ee18626ba87b6254b2ab5418'
,
'Detach: d83d5bc53967baa0ee18626ba87b6254b2ab5418'
,
stderr:
'Some error'
,
stderr:
'Some usb error'
,
));
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
command:
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
'xcrun'
,
'xcdevice'
,
'observe'
,
'--wifi'
,
],
stdout:
'Listening for all devices, on WiFi.
\n
'
'Attach: 00000001-0000000000000000
\n
'
'Detach: 00000001-0000000000000000'
,
stderr:
'Some wifi error'
,
));
));
final
Completer
<
void
>
attach1
=
Completer
<
void
>();
final
Completer
<
void
>
attach1
=
Completer
<
void
>();
final
Completer
<
void
>
attach2
=
Completer
<
void
>();
final
Completer
<
void
>
attach2
=
Completer
<
void
>();
final
Completer
<
void
>
detach1
=
Completer
<
void
>();
final
Completer
<
void
>
detach1
=
Completer
<
void
>();
final
Completer
<
void
>
attach3
=
Completer
<
void
>();
final
Completer
<
void
>
detach2
=
Completer
<
void
>();
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
// Attach: 00008027-00192736010F802E
// Attach: 00008027-00192736010F802E
// Detach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
// Detach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
xcdevice
.
observedDeviceEvents
()!.
listen
((
Map
<
XCDeviceEvent
,
String
>
event
)
{
xcdevice
.
observedDeviceEvents
()!.
listen
((
XCDeviceEventNotification
event
)
{
expect
(
event
.
length
,
1
);
if
(
event
.
eventType
==
XCDeviceEvent
.
attach
)
{
if
(
event
.
containsKey
(
XCDeviceEvent
.
attach
))
{
if
(
event
.
deviceIdentifier
==
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
)
{
if
(
event
[
XCDeviceEvent
.
attach
]
==
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
)
{
attach1
.
complete
();
attach1
.
complete
();
}
else
}
else
if
(
event
[
XCDeviceEvent
.
attach
]
==
'00008027-00192736010F802E'
)
{
if
(
event
.
deviceIdentifier
==
'00008027-00192736010F802E'
)
{
attach2
.
complete
();
attach2
.
complete
();
}
}
}
else
if
(
event
.
containsKey
(
XCDeviceEvent
.
detach
))
{
if
(
event
.
deviceIdentifier
==
'00000001-0000000000000000'
)
{
expect
(
event
[
XCDeviceEvent
.
detach
],
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
);
attach3
.
complete
();
detach1
.
complete
();
}
}
else
if
(
event
.
eventType
==
XCDeviceEvent
.
detach
)
{
if
(
event
.
deviceIdentifier
==
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
)
{
detach1
.
complete
();
}
if
(
event
.
deviceIdentifier
==
'00000001-0000000000000000'
)
{
detach2
.
complete
();
}
}
else
{
}
else
{
fail
(
'Unexpected event'
);
fail
(
'Unexpected event'
);
}
}
...
@@ -375,7 +400,10 @@ void main() {
...
@@ -375,7 +400,10 @@ void main() {
await
attach1
.
future
;
await
attach1
.
future
;
await
attach2
.
future
;
await
attach2
.
future
;
await
detach1
.
future
;
await
detach1
.
future
;
expect
(
logger
.
traceText
,
contains
(
'xcdevice observe error: Some error'
));
await
attach3
.
future
;
await
detach2
.
future
;
expect
(
logger
.
errorText
,
contains
(
'xcdevice observe --usb: Some usb error'
));
expect
(
logger
.
errorText
,
contains
(
'xcdevice observe --wifi: Some wifi error'
));
});
});
testUsingContext
(
'handles exit code'
,
()
async
{
testUsingContext
(
'handles exit code'
,
()
async
{
...
@@ -388,8 +416,21 @@ void main() {
...
@@ -388,8 +416,21 @@ void main() {
'xcrun'
,
'xcrun'
,
'xcdevice'
,
'xcdevice'
,
'observe'
,
'observe'
,
'--both'
,
'--usb'
,
],
));
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
command:
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
'xcrun'
,
'xcdevice'
,
'observe'
,
'--wifi'
,
],
],
exitCode:
1
,
));
));
final
Completer
<
void
>
doneCompleter
=
Completer
<
void
>();
final
Completer
<
void
>
doneCompleter
=
Completer
<
void
>();
...
@@ -397,7 +438,8 @@ void main() {
...
@@ -397,7 +438,8 @@ void main() {
doneCompleter
.
complete
();
doneCompleter
.
complete
();
});
});
await
doneCompleter
.
future
;
await
doneCompleter
.
future
;
expect
(
logger
.
traceText
,
contains
(
'xcdevice exited with code 0'
));
expect
(
logger
.
traceText
,
contains
(
'xcdevice observe --usb exited with code 0'
));
expect
(
logger
.
traceText
,
contains
(
'xcdevice observe --wifi exited with code 0'
));
});
});
});
});
...
@@ -418,6 +460,7 @@ void main() {
...
@@ -418,6 +460,7 @@ void main() {
'--usb'
,
'--usb'
,
deviceId
,
deviceId
,
],
],
stdout:
'Waiting for
$deviceId
to appear, on USB.
\n
'
,
));
));
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
command:
<
String
>[
command:
<
String
>[
...
@@ -431,7 +474,9 @@ void main() {
...
@@ -431,7 +474,9 @@ void main() {
'--wifi'
,
'--wifi'
,
deviceId
,
deviceId
,
],
],
stdout:
'Attach: 00000001-0000000000000000
\n
'
,
stdout:
'Waiting for
$deviceId
to appear, on WiFi.
\n
'
'Attach: 00000001-0000000000000000
\n
'
,
));
));
// Attach: 00000001-0000000000000000
// Attach: 00000001-0000000000000000
...
@@ -459,6 +504,7 @@ void main() {
...
@@ -459,6 +504,7 @@ void main() {
deviceId
,
deviceId
,
],
],
exitCode:
1
,
exitCode:
1
,
stderr:
'Some error'
,
));
));
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
command:
<
String
>[
command:
<
String
>[
...
@@ -477,6 +523,7 @@ void main() {
...
@@ -477,6 +523,7 @@ void main() {
final
XCDeviceEventNotification
?
event
=
await
xcdevice
.
waitForDeviceToConnect
(
deviceId
);
final
XCDeviceEventNotification
?
event
=
await
xcdevice
.
waitForDeviceToConnect
(
deviceId
);
expect
(
event
,
isNull
);
expect
(
event
,
isNull
);
expect
(
logger
.
errorText
,
contains
(
'xcdevice wait --usb: Some error'
));
expect
(
logger
.
traceText
,
contains
(
'xcdevice wait --usb exited with code 0'
));
expect
(
logger
.
traceText
,
contains
(
'xcdevice wait --usb exited with code 0'
));
expect
(
logger
.
traceText
,
contains
(
'xcdevice wait --wifi exited with code 0'
));
expect
(
logger
.
traceText
,
contains
(
'xcdevice wait --wifi exited with code 0'
));
expect
(
xcdevice
.
waitStreamController
?.
isClosed
,
isTrue
);
expect
(
xcdevice
.
waitStreamController
?.
isClosed
,
isTrue
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment