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 {
@override
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
Future
<
void
>
startPolling
()
async
{
...
...
@@ -75,16 +99,13 @@ class IOSDevices extends PollingDeviceDiscovery {
deviceNotifier
??=
ItemListNotifier
<
Device
>();
// Start by populating all currently attached devices.
final
List
<
Device
>
devices
=
await
pollingGetDevices
();
// Only show connected devices.
final
List
<
Device
>
filteredDevices
=
devices
.
where
((
Device
device
)
=>
device
.
isConnected
==
true
).
toList
();
deviceNotifier
!.
updateWithNewList
(
filteredDevices
);
_updateCachedDevices
(
await
pollingGetDevices
());
_updateNotifierFromCache
();
// cancel any outstanding subscriptions.
await
_observedDeviceEventsSubscription
?.
cancel
();
_observedDeviceEventsSubscription
=
xcdevice
.
observedDeviceEvents
()?.
listen
(
_
onDeviceEvent
,
onDeviceEvent
,
onError:
(
Object
error
,
StackTrace
stack
)
{
_logger
.
printTrace
(
'Process exception running xcdevice observe:
\n
$error
\n
$stack
'
);
},
onDone:
()
{
...
...
@@ -98,32 +119,89 @@ class IOSDevices extends PollingDeviceDiscovery {
);
}
Future
<
void
>
_onDeviceEvent
(
Map
<
XCDeviceEvent
,
String
>
event
)
async
{
final
XCDeviceEvent
eventType
=
event
.
containsKey
(
XCDeviceEvent
.
attach
)
?
XCDeviceEvent
.
attach
:
XCDeviceEvent
.
detach
;
final
String
?
deviceIdentifier
=
event
[
eventType
];
@visibleForTesting
Future
<
void
>
onDeviceEvent
(
XCDeviceEventNotification
event
)
async
{
final
ItemListNotifier
<
Device
>?
notifier
=
deviceNotifier
;
if
(
notifier
==
null
)
{
return
;
}
Device
?
knownDevice
;
for
(
final
Device
device
in
notifier
.
items
)
{
if
(
device
.
id
==
deviceIdentifier
)
{
if
(
device
.
id
==
event
.
deviceIdentifier
)
{
knownDevice
=
device
;
}
}
// Ignore already discovered devices (maybe populated at the beginning).
if
(
eventType
==
XCDeviceEvent
.
attach
&&
knownDevice
==
null
)
{
// There's no way to get details for an individual attached device,
// so repopulate them all.
final
List
<
Device
>
devices
=
await
pollingGetDevices
();
final
Map
<
XCDeviceEventInterface
,
bool
>
deviceObservedConnections
=
_observedConnectionsByDeviceId
[
event
.
deviceIdentifier
]
??
<
XCDeviceEventInterface
,
bool
>{
XCDeviceEventInterface
.
usb
:
false
,
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.
final
List
<
Device
>
filteredDevices
=
devices
.
where
((
Device
device
)
=>
device
.
isConnected
==
true
).
toList
();
notifier
.
updateWithNewList
(
filteredDevices
);
}
else
if
(
eventType
==
XCDeviceEvent
.
detach
&&
knownDevice
!=
null
)
{
notifier
.
removeItem
(
knownDevice
);
/// Updates notifier with devices found in the cache that are determined
/// to be connected.
void
_updateNotifierFromCache
()
{
final
ItemListNotifier
<
Device
>?
notifier
=
deviceNotifier
;
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
...
...
packages/flutter_tools/lib/src/macos/xcdevice.dart
View file @
34d2c8d0
...
...
@@ -86,7 +86,8 @@ class XCDevice {
}
void
dispose
()
{
_deviceObservationProcess
?.
kill
();
_usbDeviceObserveProcess
?.
kill
();
_wifiDeviceObserveProcess
?.
kill
();
_usbDeviceWaitProcess
?.
kill
();
_wifiDeviceWaitProcess
?.
kill
();
}
...
...
@@ -99,8 +100,10 @@ class XCDevice {
final
IProxy
_iProxy
;
List
<
Object
>?
_cachedListResults
;
Process
?
_deviceObservationProcess
;
StreamController
<
Map
<
XCDeviceEvent
,
String
>>?
_deviceIdentifierByEvent
;
Process
?
_usbDeviceObserveProcess
;
Process
?
_wifiDeviceObserveProcess
;
StreamController
<
XCDeviceEventNotification
>?
_observeStreamController
;
@visibleForTesting
StreamController
<
XCDeviceEventNotification
>?
waitStreamController
;
...
...
@@ -109,9 +112,9 @@ class XCDevice {
Process
?
_wifiDeviceWaitProcess
;
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.
_
deviceIdentifierByEvent
=
StreamController
<
Map
<
XCDeviceEvent
,
String
>
>.
broadcast
(
_
observeStreamController
=
StreamController
<
XCDeviceEventNotification
>.
broadcast
(
onListen:
_startObservingTetheredIOSDevices
,
onCancel:
_stopObservingTetheredIOSDevices
,
);
...
...
@@ -121,7 +124,7 @@ class XCDevice {
Future
<
List
<
Object
>?>
_getAllDevices
({
bool
useCache
=
false
,
required
Duration
timeout
required
Duration
timeout
,
})
async
{
if
(!
isInstalled
)
{
_logger
.
printTrace
(
"Xcode not found. Run 'flutter doctor' for more information."
);
...
...
@@ -166,14 +169,14 @@ class XCDevice {
/// Observe identifiers (UDIDs) of devices as they attach and detach.
///
/// Each attach and detach event
is a tuple of one event type
///
and identifi
er.
Stream
<
Map
<
XCDeviceEvent
,
String
>
>?
observedDeviceEvents
()
{
/// Each attach and detach event
contains information on the event type,
///
the event interface, and the device identif
er.
Stream
<
XCDeviceEventNotification
>?
observedDeviceEvents
()
{
if
(!
isInstalled
)
{
_logger
.
printTrace
(
"Xcode not found. Run 'flutter doctor' for more information."
);
return
null
;
}
return
_
deviceIdentifierByEvent
?.
stream
;
return
_
observeStreamController
?.
stream
;
}
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
...
...
@@ -183,66 +186,130 @@ class XCDevice {
Future
<
void
>
_startObservingTetheredIOSDevices
()
async
{
try
{
if
(
_
deviceObservation
Process
!=
null
)
{
if
(
_
usbDeviceObserveProcess
!=
null
||
_wifiDeviceObserve
Process
!=
null
)
{
throw
Exception
(
'xcdevice observe restart failed'
);
}
// Run in interactive mode (via script) to convince
// xcdevice it has a terminal attached in order to redirect stdout.
_deviceObservationProcess
=
await
_processUtils
.
start
(
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
...
_xcode
.
xcrunCommand
(),
'xcdevice'
,
'observe'
,
'--both'
,
],
_usbDeviceObserveProcess
=
await
_startObserveProcess
(
XCDeviceEventInterface
.
usb
,
);
final
StreamSubscription
<
String
>
stdoutSubscription
=
_deviceObservationProcess
!.
stdout
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
final
XCDeviceEventNotification
?
event
=
_processXCDeviceStdOut
(
line
,
XCDeviceEventInterface
.
usb
,
);
if
(
event
!=
null
)
{
_deviceIdentifierByEvent
?.
add
(<
XCDeviceEvent
,
String
>{
event
.
eventType
:
event
.
deviceIdentifier
,
});
}
_wifiDeviceObserveProcess
=
await
_startObserveProcess
(
XCDeviceEventInterface
.
wifi
,
);
final
Future
<
void
>
usbProcessExited
=
_usbDeviceObserveProcess
!.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice observe --usb exited with code
$exitCode
'
);
// Kill other process in case only one was killed.
_wifiDeviceObserveProcess
?.
kill
();
});
final
StreamSubscription
<
String
>
stderrSubscription
=
_deviceObservationProcess
!.
stderr
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
_
logger
.
printTrace
(
'xcdevice observe error:
$line
'
);
final
Future
<
void
>
wifiProcessExited
=
_wifiDeviceObserveProcess
!.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice observe --wifi exited with code
$exitCode
'
);
// Kill other process in case only one was killed.
_
usbDeviceObserveProcess
?.
kill
(
);
});
unawaited
(
_deviceObservationProcess
?.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice exited with code
$exitCode
'
);
u
nawaited
(
stdoutSubscription
.
cancel
());
unawaited
(
stderrSubscription
.
cancel
());
}
).
whenComplete
(()
async
{
if
(
_
deviceIdentifierByEvent
?.
hasListener
??
false
)
{
unawaited
(
Future
.
wait
(<
Future
<
void
>>[
u
sbProcessExited
,
wifiProcessExited
,
]
).
whenComplete
(()
async
{
if
(
_
observeStreamController
?.
hasListener
??
false
)
{
// 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.
_setupDeviceIdentifierByEventStream
();
}));
}
on
ProcessException
catch
(
exception
,
stackTrace
)
{
_
deviceIdentifierByEvent
?.
addError
(
exception
,
stackTrace
);
_
observeStreamController
?.
addError
(
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
(
String
line
,
XCDeviceEventInterface
eventInterface
,
...
...
@@ -275,10 +342,6 @@ class XCDevice {
return
null
;
}
void
_stopObservingTetheredIOSDevices
()
{
_deviceObservationProcess
?.
kill
();
}
/// Wait for a connect event for a specific device. Must use device's exact UDID.
///
/// To cancel this process, call [cancelWaitForDeviceToConnect].
...
...
@@ -292,72 +355,26 @@ class XCDevice {
waitStreamController
=
StreamController
<
XCDeviceEventNotification
>();
// Run in interactive mode (via script) to convince
// xcdevice it has a terminal attached in order to redirect stdout.
_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
!,
_usbDeviceWaitProcess
=
await
_startWaitProcess
(
deviceId
,
XCDeviceEventInterface
.
usb
,
);
final
StreamSubscription
<
String
>
wifiStdoutSubscription
=
_processWaitStdOut
(
_wifiDeviceWaitProcess
!,
_wifiDeviceWaitProcess
=
await
_startWaitProcess
(
deviceId
,
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
)
{
_logger
.
printTrace
(
'xcdevice wait --usb exited with code
$exitCode
'
);
// Kill other process in case only one was killed.
_wifiDeviceWaitProcess
?.
kill
();
unawaited
(
usbStdoutSubscription
.
cancel
());
unawaited
(
usbStderrSubscription
.
cancel
());
});
final
Future
<
void
>
wifiProcessExited
=
_wifiDeviceWaitProcess
!.
exitCode
.
then
((
int
status
)
{
_logger
.
printTrace
(
'xcdevice wait --wifi exited with code
$exitCode
'
);
// Kill other process in case only one was killed.
_usbDeviceWaitProcess
?.
kill
();
unawaited
(
wifiStdoutSubscription
.
cancel
());
unawaited
(
wifiStderrSubscription
.
cancel
());
});
final
Future
<
void
>
allProcessesExited
=
Future
.
wait
(
...
...
@@ -389,22 +406,33 @@ class XCDevice {
return
null
;
}
StreamSubscription
<
String
>
_processWaitStdOut
(
Process
process
,
XCDeviceEventInterface
eventInterface
,
)
{
return
process
.
stdout
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
final
XCDeviceEventNotification
?
event
=
_processXCDeviceStdOut
(
line
,
eventInterface
,
);
if
(
event
!=
null
&&
event
.
eventType
==
XCDeviceEvent
.
attach
)
{
waitStreamController
?.
add
(
event
);
}
});
Future
<
Process
>
_startWaitProcess
(
String
deviceId
,
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'
,
'wait'
,
'--
${eventInterface.name}
'
,
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
()
{
...
...
packages/flutter_tools/test/general.shard/ios/devices_test.dart
View file @
34d2c8d0
...
...
@@ -413,7 +413,7 @@ void main() {
});
testWithoutContext
(
'start polling'
,
()
async
{
final
IOSDevices
iosDevices
=
IOSDevices
(
final
TestIOSDevices
iosDevices
=
Test
IOSDevices
(
platform:
macPlatform
,
xcdevice:
xcdevice
,
iosWorkflow:
iosWorkflow
,
...
...
@@ -447,25 +447,68 @@ void main() {
expect
(
iosDevices
.
deviceNotifier
!.
items
,
isEmpty
);
expect
(
xcdevice
.
deviceEventController
.
hasListener
,
isTrue
);
xcdevice
.
deviceEventController
.
add
(<
XCDeviceEvent
,
String
>{
XCDeviceEvent
.
attach
:
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
,
});
xcdevice
.
deviceEventController
.
add
(
XCDeviceEventNotification
(
XCDeviceEvent
.
attach
,
XCDeviceEventInterface
.
usb
,
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
),
);
await
added
.
future
;
expect
(
iosDevices
.
deviceNotifier
!.
items
.
length
,
2
);
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device1
));
expect
(
iosDevices
.
deviceNotifier
!.
items
,
contains
(
device2
));
xcdevice
.
deviceEventController
.
add
(<
XCDeviceEvent
,
String
>{
XCDeviceEvent
.
detach
:
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
,
});
expect
(
iosDevices
.
eventsReceived
,
1
);
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
;
expect
(
iosDevices
.
deviceNotifier
!.
items
,
<
Device
>[
device2
]);
// Remove stream will throw over-completion if called more than once
// which proves this is ignored.
xcdevice
.
deviceEventController
.
add
(<
XCDeviceEvent
,
String
>{
XCDeviceEvent
.
detach
:
'bogus'
,
});
expect
(
iosDevices
.
eventsReceived
,
4
);
iosDevices
.
resetEventCompleter
();
xcdevice
.
deviceEventController
.
add
(
XCDeviceEventNotification
(
XCDeviceEvent
.
detach
,
XCDeviceEventInterface
.
usb
,
'bogus'
),
);
await
iosDevices
.
receivedEvent
.
future
;
expect
(
iosDevices
.
eventsReceived
,
5
);
expect
(
addedCount
,
2
);
...
...
@@ -485,7 +528,7 @@ void main() {
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
(()
{
xcdevice
.
deviceEventController
=
rescheduledStream
;
...
...
@@ -723,13 +766,35 @@ class FakeIOSApp extends Fake implements IOSApp {
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
FakeXcdevice
extends
Fake
implements
XCDevice
{
int
getAvailableIOSDevicesCount
=
0
;
final
List
<
List
<
IOSDevice
>>
devices
=
<
List
<
IOSDevice
>>[];
final
List
<
String
>
diagnostics
=
<
String
>[];
StreamController
<
Map
<
XCDeviceEvent
,
String
>>
deviceEventController
=
StreamController
<
Map
<
XCDeviceEvent
,
String
>>();
StreamController
<
XCDeviceEventNotification
>
deviceEventController
=
StreamController
<
XCDeviceEventNotification
>();
XCDeviceEventNotification
?
waitForDeviceEvent
;
@override
...
...
@@ -741,7 +806,7 @@ class FakeXcdevice extends Fake implements XCDevice {
}
@override
Stream
<
Map
<
XCDeviceEvent
,
String
>
>
observedDeviceEvents
()
{
Stream
<
XCDeviceEventNotification
>
observedDeviceEvents
()
{
return
deviceEventController
.
stream
;
}
...
...
packages/flutter_tools/test/general.shard/macos/xcode_test.dart
View file @
34d2c8d0
...
...
@@ -342,32 +342,57 @@ void main() {
'xcrun'
,
'xcdevice'
,
'observe'
,
'--both'
,
],
stdout:
'Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
\n
'
'--usb'
,
],
stdout:
'Listening for all devices, on USB.
\n
'
'Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
\n
'
'Attach: 00008027-00192736010F802E
\n
'
'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
>
attach2
=
Completer
<
void
>();
final
Completer
<
void
>
detach1
=
Completer
<
void
>();
final
Completer
<
void
>
attach3
=
Completer
<
void
>();
final
Completer
<
void
>
detach2
=
Completer
<
void
>();
// Attach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
// Attach: 00008027-00192736010F802E
// Detach: d83d5bc53967baa0ee18626ba87b6254b2ab5418
xcdevice
.
observedDeviceEvents
()!.
listen
((
Map
<
XCDeviceEvent
,
String
>
event
)
{
expect
(
event
.
length
,
1
);
if
(
event
.
containsKey
(
XCDeviceEvent
.
attach
))
{
if
(
event
[
XCDeviceEvent
.
attach
]
==
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
)
{
xcdevice
.
observedDeviceEvents
()!.
listen
((
XCDeviceEventNotification
event
)
{
if
(
event
.
eventType
==
XCDeviceEvent
.
attach
)
{
if
(
event
.
deviceIdentifier
==
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
)
{
attach1
.
complete
();
}
else
if
(
event
[
XCDeviceEvent
.
attach
]
==
'00008027-00192736010F802E'
)
{
if
(
event
.
deviceIdentifier
==
'00008027-00192736010F802E'
)
{
attach2
.
complete
();
}
}
else
if
(
event
.
containsKey
(
XCDeviceEvent
.
detach
))
{
expect
(
event
[
XCDeviceEvent
.
detach
],
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
);
detach1
.
complete
();
if
(
event
.
deviceIdentifier
==
'00000001-0000000000000000'
)
{
attach3
.
complete
();
}
}
else
if
(
event
.
eventType
==
XCDeviceEvent
.
detach
)
{
if
(
event
.
deviceIdentifier
==
'd83d5bc53967baa0ee18626ba87b6254b2ab5418'
)
{
detach1
.
complete
();
}
if
(
event
.
deviceIdentifier
==
'00000001-0000000000000000'
)
{
detach2
.
complete
();
}
}
else
{
fail
(
'Unexpected event'
);
}
...
...
@@ -375,7 +400,10 @@ void main() {
await
attach1
.
future
;
await
attach2
.
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
{
...
...
@@ -388,8 +416,21 @@ void main() {
'xcrun'
,
'xcdevice'
,
'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
>();
...
...
@@ -397,7 +438,8 @@ void main() {
doneCompleter
.
complete
();
});
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() {
'--usb'
,
deviceId
,
],
stdout:
'Waiting for
$deviceId
to appear, on USB.
\n
'
,
));
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
command:
<
String
>[
...
...
@@ -431,7 +474,9 @@ void main() {
'--wifi'
,
deviceId
,
],
stdout:
'Attach: 00000001-0000000000000000
\n
'
,
stdout:
'Waiting for
$deviceId
to appear, on WiFi.
\n
'
'Attach: 00000001-0000000000000000
\n
'
,
));
// Attach: 00000001-0000000000000000
...
...
@@ -459,6 +504,7 @@ void main() {
deviceId
,
],
exitCode:
1
,
stderr:
'Some error'
,
));
fakeProcessManager
.
addCommand
(
const
FakeCommand
(
command:
<
String
>[
...
...
@@ -477,6 +523,7 @@ void main() {
final
XCDeviceEventNotification
?
event
=
await
xcdevice
.
waitForDeviceToConnect
(
deviceId
);
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 --wifi exited with code 0'
));
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