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
67046f93
Commit
67046f93
authored
Feb 21, 2016
by
Devon Carew
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add the ability to start and stop device polling
parent
9e56fc4e
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
1098 additions
and
362 deletions
+1098
-362
adb.dart
packages/flutter_tools/lib/src/android/adb.dart
+1
-1
android_device.dart
packages/flutter_tools/lib/src/android/android_device.dart
+3
-9
utils.dart
packages/flutter_tools/lib/src/base/utils.dart
+48
-0
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+39
-146
install.dart
packages/flutter_tools/lib/src/commands/install.dart
+1
-1
trace.dart
packages/flutter_tools/lib/src/commands/trace.dart
+1
-1
device.dart
packages/flutter_tools/lib/src/device.dart
+68
-28
devices.dart
packages/flutter_tools/lib/src/ios/devices.dart
+255
-0
mac.dart
packages/flutter_tools/lib/src/ios/mac.dart
+147
-0
simulator.dart
packages/flutter_tools/lib/src/ios/simulator.dart
+0
-173
simulators.dart
packages/flutter_tools/lib/src/ios/simulators.dart
+488
-0
android_device_test.dart
packages/flutter_tools/test/android_device_test.dart
+1
-1
base_utils_test.dart
packages/flutter_tools/test/base_utils_test.dart
+37
-0
daemon_test.dart
packages/flutter_tools/test/daemon_test.dart
+6
-0
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+3
-2
No files found.
packages/flutter_tools/lib/src/android/adb.dart
View file @
67046f93
...
@@ -74,7 +74,7 @@ class Adb {
...
@@ -74,7 +74,7 @@ class Adb {
).
toList
();
).
toList
();
}
}
/// Listen to device activations and deactivations via the a
s
b server's
/// Listen to device activations and deactivations via the a
d
b server's
/// 'track-devices' command. Call cancel on the returned stream to stop
/// 'track-devices' command. Call cancel on the returned stream to stop
/// listening.
/// listening.
Stream
<
List
<
AdbDevice
>>
trackDevices
()
{
Stream
<
List
<
AdbDevice
>>
trackDevices
()
{
...
...
packages/flutter_tools/lib/src/android/
device_android
.dart
→
packages/flutter_tools/lib/src/android/
android_device
.dart
View file @
67046f93
...
@@ -29,17 +29,11 @@ const String _deviceBundlePath = '/data/local/tmp/dev.flx';
...
@@ -29,17 +29,11 @@ const String _deviceBundlePath = '/data/local/tmp/dev.flx';
// Path where the snapshot will be copied on the device.
// Path where the snapshot will be copied on the device.
const
String
_deviceSnapshotPath
=
'/data/local/tmp/dev_snapshot.bin'
;
const
String
_deviceSnapshotPath
=
'/data/local/tmp/dev_snapshot.bin'
;
class
AndroidDevice
Discovery
extends
DeviceDiscovery
{
class
AndroidDevice
s
extends
Polling
DeviceDiscovery
{
List
<
Device
>
_devices
=
<
Device
>[]
;
AndroidDevices
()
:
super
(
'AndroidDevices'
)
;
bool
get
supportsPlatform
=>
true
;
bool
get
supportsPlatform
=>
true
;
List
<
Device
>
pollingGetDevices
()
=>
getAdbDevices
();
Future
init
()
{
_devices
=
getAdbDevices
();
return
new
Future
.
value
();
}
List
<
Device
>
get
devices
=>
_devices
;
}
}
class
AndroidDevice
extends
Device
{
class
AndroidDevice
extends
Device
{
...
...
packages/flutter_tools/lib/src/base/utils.dart
0 → 100644
View file @
67046f93
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
/// A class to maintain a list of items, fire events when items are added or
/// removed, and calculate a diff of changes when a new list of items is
/// available.
class
ItemListNotifier
<
T
>
{
ItemListNotifier
()
{
_items
=
new
Set
<
T
>();
}
ItemListNotifier
.
from
(
List
<
T
>
items
)
{
_items
=
new
Set
<
T
>.
from
(
items
);
}
Set
<
T
>
_items
;
StreamController
<
T
>
_addedController
=
new
StreamController
<
T
>.
broadcast
();
StreamController
<
T
>
_removedController
=
new
StreamController
<
T
>.
broadcast
();
Stream
<
T
>
get
onAdded
=>
_addedController
.
stream
;
Stream
<
T
>
get
onRemoved
=>
_removedController
.
stream
;
List
<
T
>
get
items
=>
_items
.
toList
();
void
updateWithNewList
(
List
<
T
>
updatedList
)
{
Set
<
T
>
updatedSet
=
new
Set
<
T
>.
from
(
updatedList
);
Set
<
T
>
addedItems
=
updatedSet
.
difference
(
_items
);
Set
<
T
>
removedItems
=
_items
.
difference
(
updatedSet
);
_items
=
updatedSet
;
for
(
T
item
in
addedItems
)
_addedController
.
add
(
item
);
for
(
T
item
in
removedItems
)
_removedController
.
add
(
item
);
}
/// Close the streams.
void
dispose
()
{
_addedController
.
close
();
_removedController
.
close
();
}
}
packages/flutter_tools/lib/src/commands/daemon.dart
View file @
67046f93
...
@@ -6,15 +6,13 @@ import 'dart:async';
...
@@ -6,15 +6,13 @@ import 'dart:async';
import
'dart:convert'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:io'
;
import
'../android/adb.dart'
;
import
'../android/android_device.dart'
;
import
'../android/android_sdk.dart'
;
import
'../android/device_android.dart'
;
import
'../base/context.dart'
;
import
'../base/context.dart'
;
import
'../base/logger.dart'
;
import
'../base/logger.dart'
;
import
'../device.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../globals.dart'
;
import
'../ios/device
_io
s.dart'
;
import
'../ios/devices.dart'
;
import
'../ios/simulator.dart'
;
import
'../ios/simulator
s
.dart'
;
import
'../runner/flutter_command.dart'
;
import
'../runner/flutter_command.dart'
;
import
'run.dart'
;
import
'run.dart'
;
import
'stop.dart'
as
stop
;
import
'stop.dart'
as
stop
;
...
@@ -126,10 +124,8 @@ class Daemon {
...
@@ -126,10 +124,8 @@ class Daemon {
throw
'no domain for method:
$method
'
;
throw
'no domain for method:
$method
'
;
_domainMap
[
prefix
].
handleCommand
(
name
,
id
,
request
[
'params'
]);
_domainMap
[
prefix
].
handleCommand
(
name
,
id
,
request
[
'params'
]);
}
catch
(
error
,
trace
)
{
}
catch
(
error
)
{
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
stderr
.
writeln
(
'error handling
$request
:
$error
'
);
stderr
.
writeln
(
trace
);
}
}
}
}
...
@@ -170,8 +166,6 @@ abstract class Domain {
...
@@ -170,8 +166,6 @@ abstract class Domain {
}
}
}).
catchError
((
error
,
trace
)
{
}).
catchError
((
error
,
trace
)
{
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
stderr
.
writeln
(
"error handling '
$name
.
$command
':
$error
"
);
stderr
.
writeln
(
trace
);
});
});
}
}
...
@@ -286,166 +280,65 @@ class AppDomain extends Domain {
...
@@ -286,166 +280,65 @@ class AppDomain extends Domain {
/// This domain lets callers list and monitor connected devices.
/// This domain lets callers list and monitor connected devices.
///
///
/// It exports a `getDevices()` call, as well as firing `device.added`
,
/// It exports a `getDevices()` call, as well as firing `device.added`
and
/// `device.removed`
, and `device.changed`
events.
/// `device.removed` events.
class
DeviceDomain
extends
Domain
{
class
DeviceDomain
extends
Domain
{
DeviceDomain
(
Daemon
daemon
)
:
super
(
daemon
,
'device'
)
{
DeviceDomain
(
Daemon
daemon
)
:
super
(
daemon
,
'device'
)
{
registerHandler
(
'getDevices'
,
getDevices
);
registerHandler
(
'getDevices'
,
getDevices
);
registerHandler
(
'enable'
,
enable
);
registerHandler
(
'disable'
,
disable
);
_androidDeviceDiscovery
=
new
AndroidDeviceDiscovery
();
PollingDeviceDiscovery
deviceDiscovery
=
new
AndroidDevices
();
_androidDeviceDiscovery
.
onAdded
.
listen
((
Device
device
)
{
if
(
deviceDiscovery
.
supportsPlatform
)
sendEvent
(
'device.added'
,
_deviceToMap
(
device
));
_discoverers
.
add
(
deviceDiscovery
);
});
_androidDeviceDiscovery
.
onRemoved
.
listen
((
Device
device
)
{
sendEvent
(
'device.removed'
,
_deviceToMap
(
device
));
});
_androidDeviceDiscovery
.
onChanged
.
listen
((
Device
device
)
{
sendEvent
(
'device.changed'
,
_deviceToMap
(
device
));
});
if
(
Platform
.
isMacOS
)
{
deviceDiscovery
=
new
IOSDevices
();
_iosSimulatorDeviceDiscovery
=
new
IOSSimulatorDeviceDiscovery
();
if
(
deviceDiscovery
.
supportsPlatform
)
_iosSimulatorDeviceDiscovery
.
onAdded
.
listen
((
Device
device
)
{
_discoverers
.
add
(
deviceDiscovery
);
deviceDiscovery
=
new
IOSSimulators
();
if
(
deviceDiscovery
.
supportsPlatform
)
_discoverers
.
add
(
deviceDiscovery
);
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
discoverer
.
onAdded
.
listen
((
Device
device
)
{
sendEvent
(
'device.added'
,
_deviceToMap
(
device
));
sendEvent
(
'device.added'
,
_deviceToMap
(
device
));
});
});
_iosSimulatorDeviceDiscovery
.
onRemoved
.
listen
((
Device
device
)
{
discoverer
.
onRemoved
.
listen
((
Device
device
)
{
sendEvent
(
'device.removed'
,
_deviceToMap
(
device
));
sendEvent
(
'device.removed'
,
_deviceToMap
(
device
));
});
});
}
}
}
}
AndroidDeviceDiscovery
_androidDeviceDiscovery
;
List
<
PollingDeviceDiscovery
>
_discoverers
=
<
PollingDeviceDiscovery
>[];
IOSSimulatorDeviceDiscovery
_iosSimulatorDeviceDiscovery
;
Future
<
List
<
Device
>>
getDevices
(
dynamic
args
)
{
Future
<
List
<
Device
>>
getDevices
(
dynamic
args
)
{
List
<
Device
>
devices
=
<
Device
>[];
List
<
Device
>
devices
=
_discoverers
.
expand
((
PollingDeviceDiscovery
discoverer
)
{
devices
.
addAll
(
_androidDeviceDiscovery
.
getDevices
());
return
discoverer
.
devices
;
if
(
_iosSimulatorDeviceDiscovery
!=
null
)
}).
toList
();
devices
.
addAll
(
_iosSimulatorDeviceDiscovery
.
getDevices
());
return
new
Future
.
value
(
devices
);
return
new
Future
.
value
(
devices
);
}
}
void
dispose
()
{
/// Enable device events.
_androidDeviceDiscovery
.
dispose
();
Future
enable
(
dynamic
args
)
{
_iosSimulatorDeviceDiscovery
?.
dispose
();
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
}
discoverer
.
startPolling
();
}
class
AndroidDeviceDiscovery
{
AndroidDeviceDiscovery
()
{
_initAdb
();
if
(
_adb
!=
null
)
{
_subscription
=
_adb
.
trackDevices
().
listen
(
_handleUpdatedDevices
);
}
}
Adb
_adb
;
StreamSubscription
_subscription
;
Map
<
String
,
AndroidDevice
>
_devices
=
new
Map
<
String
,
AndroidDevice
>();
StreamController
<
Device
>
addedController
=
new
StreamController
<
Device
>.
broadcast
();
StreamController
<
Device
>
removedController
=
new
StreamController
<
Device
>.
broadcast
();
StreamController
<
Device
>
changedController
=
new
StreamController
<
Device
>.
broadcast
();
List
<
Device
>
getDevices
()
=>
_devices
.
values
.
toList
();
Stream
<
Device
>
get
onAdded
=>
addedController
.
stream
;
Stream
<
Device
>
get
onRemoved
=>
removedController
.
stream
;
Stream
<
Device
>
get
onChanged
=>
changedController
.
stream
;
void
_initAdb
()
{
if
(
_adb
==
null
)
{
_adb
=
new
Adb
(
getAdbPath
(
androidSdk
));
if
(!
_adb
.
exists
())
_adb
=
null
;
}
}
return
new
Future
.
value
();
}
}
void
_handleUpdatedDevices
(
List
<
AdbDevice
>
newDevices
)
{
/// Disable device events.
List
<
AndroidDevice
>
currentDevices
=
new
List
.
from
(
getDevices
());
Future
disable
(
dynamic
args
)
{
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
for
(
AdbDevice
device
in
newDevices
)
{
discoverer
.
stopPolling
();
AndroidDevice
androidDevice
=
_devices
[
device
.
id
];
if
(
androidDevice
==
null
)
{
// device added
androidDevice
=
new
AndroidDevice
(
device
.
id
,
productID:
device
.
productID
,
modelID:
device
.
modelID
,
deviceCodeName:
device
.
deviceCodeName
,
connected:
device
.
isAvailable
);
_devices
[
androidDevice
.
id
]
=
androidDevice
;
addedController
.
add
(
androidDevice
);
}
else
{
currentDevices
.
remove
(
androidDevice
);
// check state
if
(
androidDevice
.
isConnected
()
!=
device
.
isAvailable
)
{
androidDevice
.
setConnected
(
device
.
isAvailable
);
changedController
.
add
(
androidDevice
);
}
}
}
// device removed
for
(
AndroidDevice
device
in
currentDevices
)
{
_devices
.
remove
(
device
.
id
);
removedController
.
add
(
device
);
}
}
return
new
Future
.
value
();
}
}
void
dispose
()
{
void
dispose
()
{
_subscription
?.
cancel
();
for
(
PollingDeviceDiscovery
discoverer
in
_discoverers
)
{
}
discoverer
.
dispose
();
}
class
IOSSimulatorDeviceDiscovery
{
IOSSimulatorDeviceDiscovery
()
{
_subscription
=
SimControl
.
trackDevices
().
listen
(
_handleUpdatedDevices
);
}
StreamSubscription
<
List
<
SimDevice
>>
_subscription
;
Map
<
String
,
IOSSimulator
>
_devices
=
new
Map
<
String
,
IOSSimulator
>();
StreamController
<
Device
>
addedController
=
new
StreamController
<
Device
>.
broadcast
();
StreamController
<
Device
>
removedController
=
new
StreamController
<
Device
>.
broadcast
();
List
<
Device
>
getDevices
()
=>
_devices
.
values
.
toList
();
Stream
<
Device
>
get
onAdded
=>
addedController
.
stream
;
Stream
<
Device
>
get
onRemoved
=>
removedController
.
stream
;
void
_handleUpdatedDevices
(
List
<
SimDevice
>
newDevices
)
{
List
<
IOSSimulator
>
currentDevices
=
new
List
.
from
(
getDevices
());
for
(
SimDevice
device
in
newDevices
)
{
IOSSimulator
androidDevice
=
_devices
[
device
.
udid
];
if
(
androidDevice
==
null
)
{
// device added
androidDevice
=
new
IOSSimulator
(
device
.
udid
,
name:
device
.
name
);
_devices
[
androidDevice
.
id
]
=
androidDevice
;
addedController
.
add
(
androidDevice
);
}
else
{
currentDevices
.
remove
(
androidDevice
);
}
}
}
// device removed
for
(
IOSSimulator
device
in
currentDevices
)
{
_devices
.
remove
(
device
.
id
);
removedController
.
add
(
device
);
}
}
void
dispose
()
{
_subscription
?.
cancel
();
}
}
}
}
...
@@ -490,7 +383,7 @@ class NotifyingLogger extends Logger {
...
@@ -490,7 +383,7 @@ class NotifyingLogger extends Logger {
}
}
void
printTrace
(
String
message
)
{
void
printTrace
(
String
message
)
{
_messageController
.
add
(
new
LogMessage
(
'trace'
,
message
));
// This is a lot of traffic to send over the wire.
}
}
}
}
...
...
packages/flutter_tools/lib/src/commands/install.dart
View file @
67046f93
...
@@ -7,7 +7,7 @@ import 'dart:io';
...
@@ -7,7 +7,7 @@ import 'dart:io';
import
'../application_package.dart'
;
import
'../application_package.dart'
;
import
'../device.dart'
;
import
'../device.dart'
;
import
'../ios/simulator.dart'
;
import
'../ios/simulator
s
.dart'
;
import
'../runner/flutter_command.dart'
;
import
'../runner/flutter_command.dart'
;
class
InstallCommand
extends
FlutterCommand
{
class
InstallCommand
extends
FlutterCommand
{
...
...
packages/flutter_tools/lib/src/commands/trace.dart
View file @
67046f93
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
import
'dart:async'
;
import
'dart:async'
;
import
'../android/
device_android
.dart'
;
import
'../android/
android_device
.dart'
;
import
'../application_package.dart'
;
import
'../application_package.dart'
;
import
'../globals.dart'
;
import
'../globals.dart'
;
import
'../runner/flutter_command.dart'
;
import
'../runner/flutter_command.dart'
;
...
...
packages/flutter_tools/lib/src/device.dart
View file @
67046f93
...
@@ -4,12 +4,14 @@
...
@@ -4,12 +4,14 @@
import
'dart:async'
;
import
'dart:async'
;
import
'android/
device_android
.dart'
;
import
'android/
android_device
.dart'
;
import
'application_package.dart'
;
import
'application_package.dart'
;
import
'base/common.dart'
;
import
'base/common.dart'
;
import
'base/utils.dart'
;
import
'build_configuration.dart'
;
import
'build_configuration.dart'
;
import
'globals.dart'
;
import
'globals.dart'
;
import
'ios/device_ios.dart'
;
import
'ios/devices.dart'
;
import
'ios/simulators.dart'
;
import
'toolchain.dart'
;
import
'toolchain.dart'
;
/// A class to get all available devices.
/// A class to get all available devices.
...
@@ -18,27 +20,9 @@ class DeviceManager {
...
@@ -18,27 +20,9 @@ class DeviceManager {
/// of their methods are invoked.
/// of their methods are invoked.
DeviceManager
()
{
DeviceManager
()
{
// Register the known discoverers.
// Register the known discoverers.
_deviceDiscoverers
.
add
(
new
AndroidDeviceDiscovery
());
_deviceDiscoverers
.
add
(
new
AndroidDevices
());
_deviceDiscoverers
.
add
(
new
IOSDeviceDiscovery
());
_deviceDiscoverers
.
add
(
new
IOSDevices
());
_deviceDiscoverers
.
add
(
new
IOSSimulatorDiscovery
());
_deviceDiscoverers
.
add
(
new
IOSSimulators
());
}
Future
_init
()
{
if
(
_initedCompleter
==
null
)
{
_initedCompleter
=
new
Completer
();
Future
.
forEach
(
_deviceDiscoverers
,
(
DeviceDiscovery
discoverer
)
{
if
(!
discoverer
.
supportsPlatform
)
return
null
;
return
discoverer
.
init
();
}).
then
((
_
)
{
_initedCompleter
.
complete
();
}).
catchError
((
error
,
stackTrace
)
{
_initedCompleter
.
completeError
(
error
,
stackTrace
);
});
}
return
_initedCompleter
.
future
;
}
}
List
<
DeviceDiscovery
>
_deviceDiscoverers
=
<
DeviceDiscovery
>[];
List
<
DeviceDiscovery
>
_deviceDiscoverers
=
<
DeviceDiscovery
>[];
...
@@ -46,8 +30,6 @@ class DeviceManager {
...
@@ -46,8 +30,6 @@ class DeviceManager {
/// A user-specified device ID.
/// A user-specified device ID.
String
specifiedDeviceId
;
String
specifiedDeviceId
;
Completer
_initedCompleter
;
bool
get
hasSpecifiedDeviceId
=>
specifiedDeviceId
!=
null
;
bool
get
hasSpecifiedDeviceId
=>
specifiedDeviceId
!=
null
;
/// Return the device with the matching ID; else, complete the Future with
/// Return the device with the matching ID; else, complete the Future with
...
@@ -75,8 +57,6 @@ class DeviceManager {
...
@@ -75,8 +57,6 @@ class DeviceManager {
/// Return the list of all connected devices.
/// Return the list of all connected devices.
Future
<
List
<
Device
>>
getAllConnectedDevices
()
async
{
Future
<
List
<
Device
>>
getAllConnectedDevices
()
async
{
await
_init
();
return
_deviceDiscoverers
return
_deviceDiscoverers
.
where
((
DeviceDiscovery
discoverer
)
=>
discoverer
.
supportsPlatform
)
.
where
((
DeviceDiscovery
discoverer
)
=>
discoverer
.
supportsPlatform
)
.
expand
((
DeviceDiscovery
discoverer
)
=>
discoverer
.
devices
)
.
expand
((
DeviceDiscovery
discoverer
)
=>
discoverer
.
devices
)
...
@@ -87,10 +67,60 @@ class DeviceManager {
...
@@ -87,10 +67,60 @@ class DeviceManager {
/// An abstract class to discover and enumerate a specific type of devices.
/// An abstract class to discover and enumerate a specific type of devices.
abstract
class
DeviceDiscovery
{
abstract
class
DeviceDiscovery
{
bool
get
supportsPlatform
;
bool
get
supportsPlatform
;
Future
init
();
List
<
Device
>
get
devices
;
List
<
Device
>
get
devices
;
}
}
/// A [DeviceDiscovery] implementation that uses polling to discover device adds
/// and removals.
abstract
class
PollingDeviceDiscovery
extends
DeviceDiscovery
{
PollingDeviceDiscovery
(
this
.
name
);
static
const
Duration
_pollingDuration
=
const
Duration
(
seconds:
4
);
final
String
name
;
ItemListNotifier
<
Device
>
_items
;
Timer
_timer
;
List
<
Device
>
pollingGetDevices
();
void
startPolling
()
{
if
(
_timer
==
null
)
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>();
_timer
=
new
Timer
.
periodic
(
_pollingDuration
,
(
Timer
timer
)
{
_items
.
updateWithNewList
(
pollingGetDevices
());
});
}
}
void
stopPolling
()
{
_timer
?.
cancel
();
_timer
=
null
;
}
List
<
Device
>
get
devices
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>.
from
(
pollingGetDevices
());
return
_items
.
items
;
}
Stream
<
Device
>
get
onAdded
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>();
return
_items
.
onAdded
;
}
Stream
<
Device
>
get
onRemoved
{
if
(
_items
==
null
)
_items
=
new
ItemListNotifier
<
Device
>();
return
_items
.
onRemoved
;
}
void
dispose
()
=>
stopPolling
();
String
toString
()
=>
'
$name
device discovery'
;
}
abstract
class
Device
{
abstract
class
Device
{
Device
(
this
.
id
);
Device
(
this
.
id
);
...
@@ -139,6 +169,16 @@ abstract class Device {
...
@@ -139,6 +169,16 @@ abstract class Device {
/// Stop an app package on the current device.
/// Stop an app package on the current device.
Future
<
bool
>
stopApp
(
ApplicationPackage
app
);
Future
<
bool
>
stopApp
(
ApplicationPackage
app
);
int
get
hashCode
=>
id
.
hashCode
;
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
Device
)
return
false
;
return
id
==
other
.
id
;
}
String
toString
()
=>
'
$runtimeType
$id
'
;
String
toString
()
=>
'
$runtimeType
$id
'
;
}
}
...
...
packages/flutter_tools/lib/src/ios/devices.dart
0 → 100644
View file @
67046f93
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'../application_package.dart'
;
import
'../base/common.dart'
;
import
'../base/process.dart'
;
import
'../build_configuration.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../toolchain.dart'
;
import
'mac.dart'
;
const
String
_ideviceinstallerInstructions
=
'To work with iOS devices, please install ideviceinstaller.
\n
'
'If you use homebrew, you can install it with "
\$
brew install ideviceinstaller".'
;
class
IOSDevices
extends
PollingDeviceDiscovery
{
IOSDevices
()
:
super
(
'IOSDevices'
);
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
List
<
Device
>
pollingGetDevices
()
=>
IOSDevice
.
getAttachedDevices
();
}
class
IOSDevice
extends
Device
{
IOSDevice
(
String
id
,
{
this
.
name
})
:
super
(
id
)
{
_installerPath
=
_checkForCommand
(
'ideviceinstaller'
);
_listerPath
=
_checkForCommand
(
'idevice_id'
);
_informerPath
=
_checkForCommand
(
'ideviceinfo'
);
_debuggerPath
=
_checkForCommand
(
'idevicedebug'
);
_loggerPath
=
_checkForCommand
(
'idevicesyslog'
);
_pusherPath
=
_checkForCommand
(
'ios-deploy'
,
'To copy files to iOS devices, please install ios-deploy. '
'You can do this using homebrew as follows:
\n
'
'
\$
brew tap flutter/flutter
\n
'
'
\$
brew install ios-deploy'
);
}
String
_installerPath
;
String
get
installerPath
=>
_installerPath
;
String
_listerPath
;
String
get
listerPath
=>
_listerPath
;
String
_informerPath
;
String
get
informerPath
=>
_informerPath
;
String
_debuggerPath
;
String
get
debuggerPath
=>
_debuggerPath
;
String
_loggerPath
;
String
get
loggerPath
=>
_loggerPath
;
String
_pusherPath
;
String
get
pusherPath
=>
_pusherPath
;
final
String
name
;
bool
get
supportsStartPaused
=>
false
;
static
List
<
IOSDevice
>
getAttachedDevices
([
IOSDevice
mockIOS
])
{
if
(!
doctor
.
iosWorkflow
.
hasIdeviceId
)
return
<
IOSDevice
>[];
List
<
IOSDevice
>
devices
=
[];
for
(
String
id
in
_getAttachedDeviceIDs
(
mockIOS
))
{
String
name
=
_getDeviceName
(
id
,
mockIOS
);
devices
.
add
(
new
IOSDevice
(
id
,
name:
name
));
}
return
devices
;
}
static
Iterable
<
String
>
_getAttachedDeviceIDs
([
IOSDevice
mockIOS
])
{
String
listerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
listerPath
:
_checkForCommand
(
'idevice_id'
);
try
{
String
output
=
runSync
([
listerPath
,
'-l'
]);
return
output
.
trim
().
split
(
'
\n
'
).
where
((
String
s
)
=>
s
!=
null
&&
s
.
isNotEmpty
);
}
catch
(
e
)
{
return
<
String
>[];
}
}
static
String
_getDeviceName
(
String
deviceID
,
[
IOSDevice
mockIOS
])
{
String
informerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
informerPath
:
_checkForCommand
(
'ideviceinfo'
);
return
runSync
([
informerPath
,
'-k'
,
'DeviceName'
,
'-u'
,
deviceID
]).
trim
();
}
static
final
Map
<
String
,
String
>
_commandMap
=
{};
static
String
_checkForCommand
(
String
command
,
[
String
macInstructions
=
_ideviceinstallerInstructions
])
{
return
_commandMap
.
putIfAbsent
(
command
,
()
{
try
{
command
=
runCheckedSync
([
'which'
,
command
]).
trim
();
}
catch
(
e
)
{
if
(
Platform
.
isMacOS
)
{
printError
(
'
$command
not found.
$macInstructions
'
);
}
else
{
printError
(
'Cannot control iOS devices or simulators.
$command
is not available on your platform.'
);
}
}
return
command
;
});
}
@override
bool
installApp
(
ApplicationPackage
app
)
{
try
{
runCheckedSync
([
installerPath
,
'-i'
,
app
.
localPath
]);
return
true
;
}
catch
(
e
)
{
return
false
;
}
return
false
;
}
@override
bool
isConnected
()
=>
_getAttachedDeviceIDs
().
contains
(
id
);
@override
bool
isSupported
()
=>
true
;
@override
bool
isAppInstalled
(
ApplicationPackage
app
)
{
try
{
String
apps
=
runCheckedSync
([
installerPath
,
'--list-apps'
]);
if
(
new
RegExp
(
app
.
id
,
multiLine:
true
).
hasMatch
(
apps
))
{
return
true
;
}
}
catch
(
e
)
{
return
false
;
}
return
false
;
}
@override
Future
<
bool
>
startApp
(
ApplicationPackage
app
,
Toolchain
toolchain
,
{
String
mainPath
,
String
route
,
bool
checked:
true
,
bool
clearLogs:
false
,
bool
startPaused:
false
,
int
debugPort:
observatoryDefaultPort
,
Map
<
String
,
dynamic
>
platformArgs
})
async
{
// TODO(chinmaygarde): Use checked, mainPath, route, clearLogs.
// TODO(devoncarew): Handle startPaused, debugPort.
printTrace
(
'Building
${app.name}
for
$id
'
);
// Step 1: Install the precompiled application if necessary.
bool
buildResult
=
await
buildIOSXcodeProject
(
app
,
buildForDevice:
true
);
if
(!
buildResult
)
{
printError
(
'Could not build the precompiled application for the device.'
);
return
false
;
}
// Step 2: Check that the application exists at the specified path.
Directory
bundle
=
new
Directory
(
path
.
join
(
app
.
localPath
,
'build'
,
'Release-iphoneos'
,
'Runner.app'
));
bool
bundleExists
=
bundle
.
existsSync
();
if
(!
bundleExists
)
{
printError
(
'Could not find the built application bundle at
${bundle.path}
.'
);
return
false
;
}
// Step 3: Attempt to install the application on the device.
int
installationResult
=
await
runCommandAndStreamOutput
([
'/usr/bin/env'
,
'ios-deploy'
,
'--id'
,
id
,
'--bundle'
,
bundle
.
path
,
]);
if
(
installationResult
!=
0
)
{
printError
(
'Could not install
${bundle.path}
on
$id
.'
);
return
false
;
}
printTrace
(
'Installation successful.'
);
return
true
;
}
@override
Future
<
bool
>
stopApp
(
ApplicationPackage
app
)
async
{
// Currently we don't have a way to stop an app running on iOS.
return
false
;
}
Future
<
bool
>
pushFile
(
ApplicationPackage
app
,
String
localFile
,
String
targetFile
)
async
{
if
(
Platform
.
isMacOS
)
{
runSync
(<
String
>[
pusherPath
,
'-t'
,
'1'
,
'--bundle_id'
,
app
.
id
,
'--upload'
,
localFile
,
'--to'
,
targetFile
]);
return
true
;
}
else
{
return
false
;
}
return
false
;
}
@override
TargetPlatform
get
platform
=>
TargetPlatform
.
iOS
;
DeviceLogReader
createLogReader
()
=>
new
_IOSDeviceLogReader
(
this
);
}
class
_IOSDeviceLogReader
extends
DeviceLogReader
{
_IOSDeviceLogReader
(
this
.
device
);
final
IOSDevice
device
;
String
get
name
=>
device
.
name
;
// TODO(devoncarew): Support [clear].
Future
<
int
>
logs
({
bool
clear:
false
})
async
{
if
(!
device
.
isConnected
())
return
2
;
return
await
runCommandAndStreamOutput
(
<
String
>[
device
.
loggerPath
],
prefix:
'[
$name
] '
,
filter:
new
RegExp
(
r'Runner'
)
);
}
int
get
hashCode
=>
name
.
hashCode
;
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
_IOSDeviceLogReader
)
return
false
;
return
other
.
name
==
name
;
}
}
packages/flutter_tools/lib/src/ios/mac.dart
View file @
67046f93
...
@@ -2,8 +2,23 @@
...
@@ -2,8 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
show
JSON
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'../application_package.dart'
;
import
'../artifacts.dart'
;
import
'../base/context.dart'
;
import
'../base/context.dart'
;
import
'../base/process.dart'
;
import
'../base/process.dart'
;
import
'../globals.dart'
;
import
'../services.dart'
;
import
'setup_xcodeproj.dart'
;
String
get
homeDirectory
=>
path
.
absolute
(
Platform
.
environment
[
'HOME'
]);
// TODO(devoncarew): Refactor functionality into XCode.
const
int
kXcodeRequiredVersionMajor
=
7
;
const
int
kXcodeRequiredVersionMajor
=
7
;
const
int
kXcodeRequiredVersionMinor
=
2
;
const
int
kXcodeRequiredVersionMinor
=
2
;
...
@@ -51,3 +66,135 @@ class XCode {
...
@@ -51,3 +66,135 @@ class XCode {
return
false
;
return
false
;
}
}
}
}
Future
<
bool
>
buildIOSXcodeProject
(
ApplicationPackage
app
,
{
bool
buildForDevice
})
async
{
String
flutterProjectPath
=
Directory
.
current
.
path
;
if
(
xcodeProjectRequiresUpdate
())
{
printTrace
(
'Initializing the Xcode project.'
);
if
((
await
setupXcodeProjectHarness
(
flutterProjectPath
))
!=
0
)
{
printError
(
'Could not initialize the Xcode project.'
);
return
false
;
}
}
else
{
updateXcodeLocalProperties
(
flutterProjectPath
);
}
if
(!
_validateEngineRevision
(
app
))
return
false
;
if
(!
_checkXcodeVersion
())
return
false
;
// Before the build, all service definitions must be updated and the dylibs
// copied over to a location that is suitable for Xcodebuild to find them.
await
_addServicesToBundle
(
new
Directory
(
app
.
localPath
));
List
<
String
>
commands
=
<
String
>[
'/usr/bin/env'
,
'xcrun'
,
'xcodebuild'
,
'-target'
,
'Runner'
,
'-configuration'
,
'Release'
];
if
(
buildForDevice
)
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphoneos'
,
'-arch'
,
'arm64'
]);
}
else
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphonesimulator'
,
'-arch'
,
'x86_64'
]);
}
try
{
runCheckedSync
(
commands
,
workingDirectory:
app
.
localPath
);
return
true
;
}
catch
(
error
)
{
return
false
;
}
}
final
RegExp
_xcodeVersionRegExp
=
new
RegExp
(
r'Xcode (\d+)\..*'
);
final
String
_xcodeRequirement
=
'Xcode 7.0 or greater is required to develop for iOS.'
;
bool
_checkXcodeVersion
(
)
{
if
(!
Platform
.
isMacOS
)
return
false
;
try
{
String
version
=
runCheckedSync
(<
String
>[
'xcodebuild'
,
'-version'
]);
Match
match
=
_xcodeVersionRegExp
.
firstMatch
(
version
);
if
(
int
.
parse
(
match
[
1
])
<
7
)
{
printError
(
'Found "
${match[0]}
".
$_xcodeRequirement
'
);
return
false
;
}
}
catch
(
e
)
{
printError
(
'Cannot find "xcodebuid".
$_xcodeRequirement
'
);
return
false
;
}
return
true
;
}
bool
_validateEngineRevision
(
ApplicationPackage
app
)
{
String
skyRevision
=
ArtifactStore
.
engineRevision
;
String
iosRevision
=
_getIOSEngineRevision
(
app
);
if
(
iosRevision
!=
skyRevision
)
{
printError
(
"Error: incompatible sky_engine revision."
);
printStatus
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
false
;
}
else
{
printTrace
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
true
;
}
}
String
_getIOSEngineRevision
(
ApplicationPackage
app
)
{
File
revisionFile
=
new
File
(
path
.
join
(
app
.
localPath
,
'REVISION'
));
if
(
revisionFile
.
existsSync
())
{
return
revisionFile
.
readAsStringSync
().
trim
();
}
else
{
return
null
;
}
}
Future
_addServicesToBundle
(
Directory
bundle
)
async
{
List
<
Map
<
String
,
String
>>
services
=
[];
printTrace
(
"Trying to resolve native pub services."
);
// Step 1: Parse the service configuration yaml files present in the service
// pub packages.
await
parseServiceConfigs
(
services
);
printTrace
(
"Found
${services.length}
service definition(s)."
);
// Step 2: Copy framework dylibs to the correct spot for xcodebuild to pick up.
Directory
frameworksDirectory
=
new
Directory
(
path
.
join
(
bundle
.
path
,
"Frameworks"
));
await
_copyServiceFrameworks
(
services
,
frameworksDirectory
);
// Step 3: Copy the service definitions manifest at the correct spot for
// xcodebuild to pick up.
File
manifestFile
=
new
File
(
path
.
join
(
bundle
.
path
,
"ServiceDefinitions.json"
));
_copyServiceDefinitionsManifest
(
services
,
manifestFile
);
}
Future
_copyServiceFrameworks
(
List
<
Map
<
String
,
String
>>
services
,
Directory
frameworksDirectory
)
async
{
printTrace
(
"Copying service frameworks to '
${path.absolute(frameworksDirectory.path)}
'."
);
frameworksDirectory
.
createSync
(
recursive:
true
);
for
(
Map
<
String
,
String
>
service
in
services
)
{
String
dylibPath
=
await
getServiceFromUrl
(
service
[
'ios-framework'
],
service
[
'root'
],
service
[
'name'
]);
File
dylib
=
new
File
(
dylibPath
);
printTrace
(
"Copying
${dylib.path}
into bundle."
);
if
(!
dylib
.
existsSync
())
{
printError
(
"The service dylib '
${dylib.path}
' does not exist."
);
continue
;
}
// Shell out so permissions on the dylib are preserved.
runCheckedSync
([
'/bin/cp'
,
dylib
.
path
,
frameworksDirectory
.
path
]);
}
}
void
_copyServiceDefinitionsManifest
(
List
<
Map
<
String
,
String
>>
services
,
File
manifest
)
{
printTrace
(
"Creating service definitions manifest at '
${manifest.path}
'"
);
List
<
Map
<
String
,
String
>>
jsonServices
=
services
.
map
((
Map
<
String
,
String
>
service
)
=>
{
'name'
:
service
[
'name'
],
// Since we have already moved it to the Frameworks directory. Strip away
// the directory and basenames.
'framework'
:
path
.
basenameWithoutExtension
(
service
[
'ios-framework'
])
}).
toList
();
Map
<
String
,
dynamic
>
json
=
{
'services'
:
jsonServices
};
manifest
.
writeAsStringSync
(
JSON
.
encode
(
json
),
mode:
FileMode
.
WRITE
,
flush:
true
);
}
packages/flutter_tools/lib/src/ios/simulator.dart
deleted
100644 → 0
View file @
9e56fc4e
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
show
JSON
;
import
'dart:io'
;
import
'../base/process.dart'
;
import
'../globals.dart'
;
const
String
_xcrunPath
=
'/usr/bin/xcrun'
;
const
String
_simulatorPath
=
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator'
;
/// A wrapper around the `simctl` command line tool.
class
SimControl
{
static
Future
<
bool
>
boot
({
String
deviceId
})
async
{
if
(
_isAnyConnected
())
return
true
;
if
(
deviceId
==
null
)
{
runDetached
([
_simulatorPath
]);
Future
<
bool
>
checkConnection
([
int
attempts
=
20
])
async
{
if
(
attempts
==
0
)
{
printStatus
(
'Timed out waiting for iOS Simulator to boot.'
);
return
false
;
}
if
(!
_isAnyConnected
())
{
printStatus
(
'Waiting for iOS Simulator to boot...'
);
return
await
new
Future
.
delayed
(
new
Duration
(
milliseconds:
500
),
()
=>
checkConnection
(
attempts
-
1
)
);
}
return
true
;
}
return
await
checkConnection
();
}
else
{
try
{
runCheckedSync
([
_xcrunPath
,
'simctl'
,
'boot'
,
deviceId
]);
return
true
;
}
catch
(
e
)
{
printError
(
'Unable to boot iOS Simulator
$deviceId
: '
,
e
);
return
false
;
}
}
return
false
;
}
/// Returns a list of all available devices, both potential and connected.
static
List
<
SimDevice
>
getDevices
()
{
// {
// "devices" : {
// "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
// {
// "state" : "Shutdown",
// "availability" : " (unavailable, runtime profile not found)",
// "name" : "iPhone 4s",
// "udid" : "1913014C-6DCB-485D-AC6B-7CD76D322F5B"
// },
// ...
List
<
String
>
args
=
<
String
>[
'simctl'
,
'list'
,
'--json'
,
'devices'
];
printTrace
(
'
$_xcrunPath
${args.join(' ')}
'
);
ProcessResult
results
=
Process
.
runSync
(
_xcrunPath
,
args
);
if
(
results
.
exitCode
!=
0
)
{
printError
(
'Error executing simctl:
${results.exitCode}
\n
${results.stderr}
'
);
return
<
SimDevice
>[];
}
List
<
SimDevice
>
devices
=
<
SimDevice
>[];
Map
<
String
,
Map
<
String
,
dynamic
>>
data
=
JSON
.
decode
(
results
.
stdout
);
Map
<
String
,
dynamic
>
devicesSection
=
data
[
'devices'
];
for
(
String
deviceCategory
in
devicesSection
.
keys
)
{
List
<
dynamic
>
devicesData
=
devicesSection
[
deviceCategory
];
for
(
Map
<
String
,
String
>
data
in
devicesData
)
{
devices
.
add
(
new
SimDevice
(
deviceCategory
,
data
));
}
}
return
devices
;
}
/// Returns all the connected simulator devices.
static
List
<
SimDevice
>
getConnectedDevices
()
{
return
getDevices
().
where
((
SimDevice
device
)
=>
device
.
isBooted
).
toList
();
}
static
StreamController
<
List
<
SimDevice
>>
_trackDevicesControler
;
/// Listens to changes in the set of connected devices. The implementation
/// currently uses polling. Callers should be careful to call cancel() on any
/// stream subscription when finished.
///
/// TODO(devoncarew): We could investigate using the usbmuxd protocol directly.
static
Stream
<
List
<
SimDevice
>>
trackDevices
()
{
if
(
_trackDevicesControler
==
null
)
{
Timer
timer
;
Set
<
String
>
deviceIds
=
new
Set
<
String
>();
_trackDevicesControler
=
new
StreamController
.
broadcast
(
onListen:
()
{
timer
=
new
Timer
.
periodic
(
new
Duration
(
seconds:
4
),
(
Timer
timer
)
{
List
<
SimDevice
>
devices
=
getConnectedDevices
();
if
(
_updateDeviceIds
(
devices
,
deviceIds
))
{
_trackDevicesControler
.
add
(
devices
);
}
});
},
onCancel:
()
{
timer
?.
cancel
();
deviceIds
.
clear
();
}
);
}
return
_trackDevicesControler
.
stream
;
}
/// Update the cached set of device IDs and return whether there were any changes.
static
bool
_updateDeviceIds
(
List
<
SimDevice
>
devices
,
Set
<
String
>
deviceIds
)
{
Set
<
String
>
newIds
=
new
Set
<
String
>.
from
(
devices
.
map
((
SimDevice
device
)
=>
device
.
udid
));
bool
changed
=
false
;
for
(
String
id
in
newIds
)
{
if
(!
deviceIds
.
contains
(
id
))
changed
=
true
;
}
for
(
String
id
in
deviceIds
)
{
if
(!
newIds
.
contains
(
id
))
changed
=
true
;
}
deviceIds
.
clear
();
deviceIds
.
addAll
(
newIds
);
return
changed
;
}
static
bool
_isAnyConnected
()
=>
getConnectedDevices
().
isNotEmpty
;
static
void
install
(
String
deviceId
,
String
appPath
)
{
runCheckedSync
([
_xcrunPath
,
'simctl'
,
'install'
,
deviceId
,
appPath
]);
}
static
void
launch
(
String
deviceId
,
String
appIdentifier
,
[
List
<
String
>
launchArgs
])
{
List
<
String
>
args
=
[
_xcrunPath
,
'simctl'
,
'launch'
,
deviceId
,
appIdentifier
];
if
(
launchArgs
!=
null
)
args
.
addAll
(
launchArgs
);
runCheckedSync
(
args
);
}
}
class
SimDevice
{
SimDevice
(
this
.
category
,
this
.
data
);
final
String
category
;
final
Map
<
String
,
String
>
data
;
String
get
state
=>
data
[
'state'
];
String
get
availability
=>
data
[
'availability'
];
String
get
name
=>
data
[
'name'
];
String
get
udid
=>
data
[
'udid'
];
bool
get
isBooted
=>
state
==
'Booted'
;
}
packages/flutter_tools/lib/src/ios/
device_io
s.dart
→
packages/flutter_tools/lib/src/ios/
simulator
s.dart
View file @
67046f93
...
@@ -3,248 +3,188 @@
...
@@ -3,248 +3,188 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:convert'
show
JSON
;
import
'dart:io'
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'package:path/path.dart'
as
path
;
import
'../application_package.dart'
;
import
'../application_package.dart'
;
import
'../artifacts.dart'
;
import
'../base/common.dart'
;
import
'../base/common.dart'
;
import
'../base/process.dart'
;
import
'../base/process.dart'
;
import
'../build_configuration.dart'
;
import
'../build_configuration.dart'
;
import
'../device.dart'
;
import
'../device.dart'
;
import
'../globals.dart'
;
import
'../globals.dart'
;
import
'../ios/setup_xcodeproj.dart'
;
import
'../services.dart'
;
import
'../toolchain.dart'
;
import
'../toolchain.dart'
;
import
'
simulator
.dart'
;
import
'
mac
.dart'
;
const
String
_ideviceinstallerInstructions
=
const
String
_xcrunPath
=
'/usr/bin/xcrun'
;
'To work with iOS devices, please install ideviceinstaller.
\n
'
'If you use homebrew, you can install it with "
\$
brew install ideviceinstaller".'
;
c
lass
IOSDeviceDiscovery
extends
DeviceDiscovery
{
c
onst
String
_simulatorPath
=
List
<
Device
>
_devices
=
<
Device
>[]
;
'/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator'
;
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
class
IOSSimulators
extends
PollingDeviceDiscovery
{
IOSSimulators
()
:
super
(
'IOSSimulators'
);
Future
init
()
{
_devices
=
IOSDevice
.
getAttachedDevices
();
return
new
Future
.
value
();
}
List
<
Device
>
get
devices
=>
_devices
;
}
class
IOSSimulatorDiscovery
extends
DeviceDiscovery
{
List
<
Device
>
_devices
=
<
Device
>[];
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
bool
get
supportsPlatform
=>
Platform
.
isMacOS
;
List
<
Device
>
pollingGetDevices
()
=>
IOSSimulator
.
getAttachedDevices
();
Future
init
()
{
_devices
=
IOSSimulator
.
getAttachedDevices
();
return
new
Future
.
value
();
}
List
<
Device
>
get
devices
=>
_devices
;
}
}
class
IOSDevice
extends
Device
{
/// A wrapper around the `simctl` command line tool.
IOSDevice
(
String
id
,
{
this
.
name
})
:
super
(
id
)
{
class
SimControl
{
_installerPath
=
_checkForCommand
(
'ideviceinstaller'
);
static
Future
<
bool
>
boot
({
String
deviceId
})
async
{
_listerPath
=
_checkForCommand
(
'idevice_id'
);
if
(
_isAnyConnected
())
_informerPath
=
_checkForCommand
(
'ideviceinfo'
);
return
true
;
_debuggerPath
=
_checkForCommand
(
'idevicedebug'
);
_loggerPath
=
_checkForCommand
(
'idevicesyslog'
);
_pusherPath
=
_checkForCommand
(
'ios-deploy'
,
'To copy files to iOS devices, please install ios-deploy. '
'You can do this using homebrew as follows:
\n
'
'
\$
brew tap flutter/flutter
\n
'
'
\$
brew install ios-deploy'
);
}
String
_installerPath
;
String
get
installerPath
=>
_installerPath
;
String
_listerPath
;
String
get
listerPath
=>
_listerPath
;
String
_informerPath
;
String
get
informerPath
=>
_informerPath
;
String
_debuggerPath
;
if
(
deviceId
==
null
)
{
String
get
debuggerPath
=>
_debuggerPath
;
runDetached
([
_simulatorPath
]);
Future
<
bool
>
checkConnection
([
int
attempts
=
20
])
async
{
if
(
attempts
==
0
)
{
printStatus
(
'Timed out waiting for iOS Simulator to boot.'
);
return
false
;
}
if
(!
_isAnyConnected
())
{
printStatus
(
'Waiting for iOS Simulator to boot...'
);
return
await
new
Future
.
delayed
(
new
Duration
(
milliseconds:
500
),
()
=>
checkConnection
(
attempts
-
1
)
);
}
return
true
;
}
return
await
checkConnection
();
}
else
{
try
{
runCheckedSync
([
_xcrunPath
,
'simctl'
,
'boot'
,
deviceId
]);
return
true
;
}
catch
(
e
)
{
printError
(
'Unable to boot iOS Simulator
$deviceId
: '
,
e
);
return
false
;
}
}
String
_loggerPath
;
return
false
;
String
get
loggerPath
=>
_loggerPath
;
}
String
_pusherPath
;
/// Returns a list of all available devices, both potential and connected.
String
get
pusherPath
=>
_pusherPath
;
static
List
<
SimDevice
>
getDevices
()
{
// {
// "devices" : {
// "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
// {
// "state" : "Shutdown",
// "availability" : " (unavailable, runtime profile not found)",
// "name" : "iPhone 4s",
// "udid" : "1913014C-6DCB-485D-AC6B-7CD76D322F5B"
// },
// ...
List
<
String
>
args
=
<
String
>[
'simctl'
,
'list'
,
'--json'
,
'devices'
];
printTrace
(
'
$_xcrunPath
${args.join(' ')}
'
);
ProcessResult
results
=
Process
.
runSync
(
_xcrunPath
,
args
);
if
(
results
.
exitCode
!=
0
)
{
printError
(
'Error executing simctl:
${results.exitCode}
\n
${results.stderr}
'
);
return
<
SimDevice
>[];
}
final
String
name
;
List
<
SimDevice
>
devices
=
<
SimDevice
>[]
;
bool
get
supportsStartPaused
=>
false
;
Map
<
String
,
Map
<
String
,
dynamic
>>
data
=
JSON
.
decode
(
results
.
stdout
);
Map
<
String
,
dynamic
>
devicesSection
=
data
[
'devices'
];
static
List
<
IOSDevice
>
getAttachedDevices
([
IOSDevice
mockIOS
])
{
for
(
String
deviceCategory
in
devicesSection
.
keys
)
{
if
(!
doctor
.
iosWorkflow
.
hasIdeviceId
)
List
<
dynamic
>
devicesData
=
devicesSection
[
deviceCategory
];
return
<
IOSDevice
>[];
List
<
IOSDevice
>
devices
=
[];
for
(
Map
<
String
,
String
>
data
in
devicesData
)
{
for
(
String
id
in
_getAttachedDeviceIDs
(
mockIOS
))
{
devices
.
add
(
new
SimDevice
(
deviceCategory
,
data
));
String
name
=
_getDeviceName
(
id
,
mockIOS
);
}
devices
.
add
(
new
IOSDevice
(
id
,
name:
name
));
}
}
return
devices
;
return
devices
;
}
}
static
Iterable
<
String
>
_getAttachedDeviceIDs
([
IOSDevice
mockIOS
])
{
/// Returns all the connected simulator devices.
String
listerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
listerPath
:
_checkForCommand
(
'idevice_id'
);
static
List
<
SimDevice
>
getConnectedDevices
()
{
try
{
return
getDevices
().
where
((
SimDevice
device
)
=>
device
.
isBooted
).
toList
();
String
output
=
runSync
([
listerPath
,
'-l'
]);
return
output
.
trim
().
split
(
'
\n
'
).
where
((
String
s
)
=>
s
!=
null
&&
s
.
isNotEmpty
);
}
catch
(
e
)
{
return
<
String
>[];
}
}
}
static
String
_getDeviceName
(
String
deviceID
,
[
IOSDevice
mockIOS
])
{
static
StreamController
<
List
<
SimDevice
>>
_trackDevicesControler
;
String
informerPath
=
(
mockIOS
!=
null
)
?
mockIOS
.
informerPath
:
_checkForCommand
(
'ideviceinfo'
);
return
runSync
([
informerPath
,
'-k'
,
'DeviceName'
,
'-u'
,
deviceID
]).
trim
();
}
static
final
Map
<
String
,
String
>
_commandMap
=
{};
/// Listens to changes in the set of connected devices. The implementation
static
String
_checkForCommand
(
/// currently uses polling. Callers should be careful to call cancel() on any
String
command
,
[
/// stream subscription when finished.
String
macInstructions
=
_ideviceinstallerInstructions
///
])
{
/// TODO(devoncarew): We could investigate using the usbmuxd protocol directly.
return
_commandMap
.
putIfAbsent
(
command
,
()
{
static
Stream
<
List
<
SimDevice
>>
trackDevices
()
{
try
{
if
(
_trackDevicesControler
==
null
)
{
command
=
runCheckedSync
([
'which'
,
command
]).
trim
();
Timer
timer
;
}
catch
(
e
)
{
Set
<
String
>
deviceIds
=
new
Set
<
String
>();
if
(
Platform
.
isMacOS
)
{
printError
(
'
$command
not found.
$macInstructions
'
);
}
else
{
printError
(
'Cannot control iOS devices or simulators.
$command
is not available on your platform.'
);
}
}
return
command
;
});
}
@override
_trackDevicesControler
=
new
StreamController
.
broadcast
(
bool
installApp
(
ApplicationPackage
app
)
{
onListen:
()
{
try
{
timer
=
new
Timer
.
periodic
(
new
Duration
(
seconds:
4
),
(
Timer
timer
)
{
runCheckedSync
([
installerPath
,
'-i'
,
app
.
localPath
]);
List
<
SimDevice
>
devices
=
getConnectedDevices
();
return
true
;
}
catch
(
e
)
{
return
false
;
}
return
false
;
}
@override
if
(
_updateDeviceIds
(
devices
,
deviceIds
))
{
bool
isConnected
()
=>
_getAttachedDeviceIDs
().
contains
(
id
);
_trackDevicesControler
.
add
(
devices
);
}
@override
});
bool
isSupported
()
=>
true
;
},
onCancel:
()
{
timer
?.
cancel
();
@override
deviceIds
.
clear
();
bool
isAppInstalled
(
ApplicationPackage
app
)
{
}
try
{
);
String
apps
=
runCheckedSync
([
installerPath
,
'--list-apps'
]);
if
(
new
RegExp
(
app
.
id
,
multiLine:
true
).
hasMatch
(
apps
))
{
return
true
;
}
}
catch
(
e
)
{
return
false
;
}
}
return
false
;
return
_trackDevicesControler
.
stream
;
}
}
@override
/// Update the cached set of device IDs and return whether there were any changes.
Future
<
bool
>
startApp
(
static
bool
_updateDeviceIds
(
List
<
SimDevice
>
devices
,
Set
<
String
>
deviceIds
)
{
ApplicationPackage
app
,
Set
<
String
>
newIds
=
new
Set
<
String
>.
from
(
devices
.
map
((
SimDevice
device
)
=>
device
.
udid
));
Toolchain
toolchain
,
{
String
mainPath
,
String
route
,
bool
checked:
true
,
bool
clearLogs:
false
,
bool
startPaused:
false
,
int
debugPort:
observatoryDefaultPort
,
Map
<
String
,
dynamic
>
platformArgs
})
async
{
// TODO(chinmaygarde): Use checked, mainPath, route, clearLogs.
// TODO(devoncarew): Handle startPaused, debugPort.
printTrace
(
'Building
${app.name}
for
$id
'
);
// Step 1: Install the precompiled application if necessary.
bool
changed
=
false
;
bool
buildResult
=
await
_buildIOSXcodeProject
(
app
,
buildForDevice:
true
);
if
(!
buildResult
)
{
printError
(
'Could not build the precompiled application for the device.'
);
return
false
;
}
// Step 2: Check that the application exists at the specified path.
for
(
String
id
in
newIds
)
{
Directory
bundle
=
new
Directory
(
path
.
join
(
app
.
localPath
,
'build'
,
'Release-iphoneos'
,
'Runner.app'
));
if
(!
deviceIds
.
contains
(
id
))
bool
bundleExists
=
bundle
.
existsSync
();
changed
=
true
;
if
(!
bundleExists
)
{
printError
(
'Could not find the built application bundle at
${bundle.path}
.'
);
return
false
;
}
}
// Step 3: Attempt to install the application on the device.
for
(
String
id
in
deviceIds
)
{
int
installationResult
=
await
runCommandAndStreamOutput
([
if
(!
newIds
.
contains
(
id
))
'/usr/bin/env'
,
changed
=
true
;
'ios-deploy'
,
'--id'
,
id
,
'--bundle'
,
bundle
.
path
,
]);
if
(
installationResult
!=
0
)
{
printError
(
'Could not install
${bundle.path}
on
$id
.'
);
return
false
;
}
}
printTrace
(
'Installation successful.'
);
deviceIds
.
clear
();
return
true
;
deviceIds
.
addAll
(
newIds
);
return
changed
;
}
}
@override
static
bool
_isAnyConnected
()
=>
getConnectedDevices
().
isNotEmpty
;
Future
<
bool
>
stopApp
(
ApplicationPackage
app
)
async
{
// Currently we don't have a way to stop an app running on iOS.
static
void
install
(
String
deviceId
,
String
appPath
)
{
r
eturn
false
;
r
unCheckedSync
([
_xcrunPath
,
'simctl'
,
'install'
,
deviceId
,
appPath
])
;
}
}
Future
<
bool
>
pushFile
(
ApplicationPackage
app
,
String
localFile
,
String
targetFile
)
async
{
static
void
launch
(
String
deviceId
,
String
appIdentifier
,
[
List
<
String
>
launchArgs
])
{
if
(
Platform
.
isMacOS
)
{
List
<
String
>
args
=
[
_xcrunPath
,
'simctl'
,
'launch'
,
deviceId
,
appIdentifier
];
runSync
(<
String
>[
if
(
launchArgs
!=
null
)
pusherPath
,
args
.
addAll
(
launchArgs
);
'-t'
,
runCheckedSync
(
args
);
'1'
,
'--bundle_id'
,
app
.
id
,
'--upload'
,
localFile
,
'--to'
,
targetFile
]);
return
true
;
}
else
{
return
false
;
}
return
false
;
}
}
}
@override
class
SimDevice
{
TargetPlatform
get
platform
=>
TargetPlatform
.
iOS
;
SimDevice
(
this
.
category
,
this
.
data
);
final
String
category
;
final
Map
<
String
,
String
>
data
;
DeviceLogReader
createLogReader
()
=>
new
_IOSDeviceLogReader
(
this
);
String
get
state
=>
data
[
'state'
];
String
get
availability
=>
data
[
'availability'
];
String
get
name
=>
data
[
'name'
];
String
get
udid
=>
data
[
'udid'
];
bool
get
isBooted
=>
state
==
'Booted'
;
}
}
class
IOSSimulator
extends
Device
{
class
IOSSimulator
extends
Device
{
...
@@ -264,7 +204,7 @@ class IOSSimulator extends Device {
...
@@ -264,7 +204,7 @@ class IOSSimulator extends Device {
String
get
xcrunPath
=>
path
.
join
(
'/usr'
,
'bin'
,
'xcrun'
);
String
get
xcrunPath
=>
path
.
join
(
'/usr'
,
'bin'
,
'xcrun'
);
String
_getSimulatorPath
()
{
String
_getSimulatorPath
()
{
return
path
.
join
(
_
homeDirectory
,
'Library'
,
'Developer'
,
'CoreSimulator'
,
'Devices'
,
id
);
return
path
.
join
(
homeDirectory
,
'Library'
,
'Developer'
,
'CoreSimulator'
,
'Devices'
,
id
);
}
}
String
_getSimulatorAppHomeDirectory
(
ApplicationPackage
app
)
{
String
_getSimulatorAppHomeDirectory
(
ApplicationPackage
app
)
{
...
@@ -378,7 +318,7 @@ class IOSSimulator extends Device {
...
@@ -378,7 +318,7 @@ class IOSSimulator extends Device {
this
.
clearLogs
();
this
.
clearLogs
();
// Step 1: Build the Xcode project.
// Step 1: Build the Xcode project.
bool
buildResult
=
await
_
buildIOSXcodeProject
(
app
,
buildForDevice:
false
);
bool
buildResult
=
await
buildIOSXcodeProject
(
app
,
buildForDevice:
false
);
if
(!
buildResult
)
{
if
(!
buildResult
)
{
printError
(
'Could not build the application for the simulator.'
);
printError
(
'Could not build the application for the simulator.'
);
return
false
;
return
false
;
...
@@ -437,7 +377,7 @@ class IOSSimulator extends Device {
...
@@ -437,7 +377,7 @@ class IOSSimulator extends Device {
}
}
String
get
logFilePath
{
String
get
logFilePath
{
return
path
.
join
(
_
homeDirectory
,
'Library'
,
'Logs'
,
'CoreSimulator'
,
id
,
'system.log'
);
return
path
.
join
(
homeDirectory
,
'Library'
,
'Logs'
,
'CoreSimulator'
,
id
,
'system.log'
);
}
}
@override
@override
...
@@ -461,36 +401,6 @@ class IOSSimulator extends Device {
...
@@ -461,36 +401,6 @@ class IOSSimulator extends Device {
}
}
}
}
class
_IOSDeviceLogReader
extends
DeviceLogReader
{
_IOSDeviceLogReader
(
this
.
device
);
final
IOSDevice
device
;
String
get
name
=>
device
.
name
;
// TODO(devoncarew): Support [clear].
Future
<
int
>
logs
({
bool
clear:
false
})
async
{
if
(!
device
.
isConnected
())
return
2
;
return
await
runCommandAndStreamOutput
(
<
String
>[
device
.
loggerPath
],
prefix:
'[
$name
] '
,
filter:
new
RegExp
(
r'Runner'
)
);
}
int
get
hashCode
=>
name
.
hashCode
;
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
_IOSDeviceLogReader
)
return
false
;
return
other
.
name
==
name
;
}
}
class
_IOSSimulatorLogReader
extends
DeviceLogReader
{
class
_IOSSimulatorLogReader
extends
DeviceLogReader
{
_IOSSimulatorLogReader
(
this
.
device
);
_IOSSimulatorLogReader
(
this
.
device
);
...
@@ -576,137 +486,3 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
...
@@ -576,137 +486,3 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
return
other
.
device
.
logFilePath
==
device
.
logFilePath
;
return
other
.
device
.
logFilePath
==
device
.
logFilePath
;
}
}
}
}
final
RegExp
_xcodeVersionRegExp
=
new
RegExp
(
r'Xcode (\d+)\..*'
);
final
String
_xcodeRequirement
=
'Xcode 7.0 or greater is required to develop for iOS.'
;
String
get
_homeDirectory
=>
path
.
absolute
(
Platform
.
environment
[
'HOME'
]);
bool
_checkXcodeVersion
(
)
{
if
(!
Platform
.
isMacOS
)
return
false
;
try
{
String
version
=
runCheckedSync
(<
String
>[
'xcodebuild'
,
'-version'
]);
Match
match
=
_xcodeVersionRegExp
.
firstMatch
(
version
);
if
(
int
.
parse
(
match
[
1
])
<
7
)
{
printError
(
'Found "
${match[0]}
".
$_xcodeRequirement
'
);
return
false
;
}
}
catch
(
e
)
{
printError
(
'Cannot find "xcodebuid".
$_xcodeRequirement
'
);
return
false
;
}
return
true
;
}
bool
_validateEngineRevision
(
ApplicationPackage
app
)
{
String
skyRevision
=
ArtifactStore
.
engineRevision
;
String
iosRevision
=
_getIOSEngineRevision
(
app
);
if
(
iosRevision
!=
skyRevision
)
{
printError
(
"Error: incompatible sky_engine revision."
);
printStatus
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
false
;
}
else
{
printTrace
(
'sky_engine revision:
$skyRevision
, iOS engine revision:
$iosRevision
'
);
return
true
;
}
}
String
_getIOSEngineRevision
(
ApplicationPackage
app
)
{
File
revisionFile
=
new
File
(
path
.
join
(
app
.
localPath
,
'REVISION'
));
if
(
revisionFile
.
existsSync
())
{
return
revisionFile
.
readAsStringSync
().
trim
();
}
else
{
return
null
;
}
}
Future
<
bool
>
_buildIOSXcodeProject
(
ApplicationPackage
app
,
{
bool
buildForDevice
})
async
{
String
flutterProjectPath
=
Directory
.
current
.
path
;
if
(
xcodeProjectRequiresUpdate
())
{
printTrace
(
'Initializing the Xcode project.'
);
if
((
await
setupXcodeProjectHarness
(
flutterProjectPath
))
!=
0
)
{
printError
(
'Could not initialize the Xcode project.'
);
return
false
;
}
}
else
{
updateXcodeLocalProperties
(
flutterProjectPath
);
}
if
(!
_validateEngineRevision
(
app
))
return
false
;
if
(!
_checkXcodeVersion
())
return
false
;
// Before the build, all service definitions must be updated and the dylibs
// copied over to a location that is suitable for Xcodebuild to find them.
await
_addServicesToBundle
(
new
Directory
(
app
.
localPath
));
List
<
String
>
commands
=
<
String
>[
'/usr/bin/env'
,
'xcrun'
,
'xcodebuild'
,
'-target'
,
'Runner'
,
'-configuration'
,
'Release'
];
if
(
buildForDevice
)
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphoneos'
,
'-arch'
,
'arm64'
]);
}
else
{
commands
.
addAll
(<
String
>[
'-sdk'
,
'iphonesimulator'
,
'-arch'
,
'x86_64'
]);
}
try
{
runCheckedSync
(
commands
,
workingDirectory:
app
.
localPath
);
return
true
;
}
catch
(
error
)
{
return
false
;
}
}
Future
_addServicesToBundle
(
Directory
bundle
)
async
{
List
<
Map
<
String
,
String
>>
services
=
[];
printTrace
(
"Trying to resolve native pub services."
);
// Step 1: Parse the service configuration yaml files present in the service
// pub packages.
await
parseServiceConfigs
(
services
);
printTrace
(
"Found
${services.length}
service definition(s)."
);
// Step 2: Copy framework dylibs to the correct spot for xcodebuild to pick up.
Directory
frameworksDirectory
=
new
Directory
(
path
.
join
(
bundle
.
path
,
"Frameworks"
));
await
_copyServiceFrameworks
(
services
,
frameworksDirectory
);
// Step 3: Copy the service definitions manifest at the correct spot for
// xcodebuild to pick up.
File
manifestFile
=
new
File
(
path
.
join
(
bundle
.
path
,
"ServiceDefinitions.json"
));
_copyServiceDefinitionsManifest
(
services
,
manifestFile
);
}
Future
_copyServiceFrameworks
(
List
<
Map
<
String
,
String
>>
services
,
Directory
frameworksDirectory
)
async
{
printTrace
(
"Copying service frameworks to '
${path.absolute(frameworksDirectory.path)}
'."
);
frameworksDirectory
.
createSync
(
recursive:
true
);
for
(
Map
<
String
,
String
>
service
in
services
)
{
String
dylibPath
=
await
getServiceFromUrl
(
service
[
'ios-framework'
],
service
[
'root'
],
service
[
'name'
]);
File
dylib
=
new
File
(
dylibPath
);
printTrace
(
"Copying
${dylib.path}
into bundle."
);
if
(!
dylib
.
existsSync
())
{
printError
(
"The service dylib '
${dylib.path}
' does not exist."
);
continue
;
}
// Shell out so permissions on the dylib are preserved.
runCheckedSync
([
'/bin/cp'
,
dylib
.
path
,
frameworksDirectory
.
path
]);
}
}
void
_copyServiceDefinitionsManifest
(
List
<
Map
<
String
,
String
>>
services
,
File
manifest
)
{
printTrace
(
"Creating service definitions manifest at '
${manifest.path}
'"
);
List
<
Map
<
String
,
String
>>
jsonServices
=
services
.
map
((
Map
<
String
,
String
>
service
)
=>
{
'name'
:
service
[
'name'
],
// Since we have already moved it to the Frameworks directory. Strip away
// the directory and basenames.
'framework'
:
path
.
basenameWithoutExtension
(
service
[
'ios-framework'
])
}).
toList
();
Map
<
String
,
dynamic
>
json
=
{
'services'
:
jsonServices
};
manifest
.
writeAsStringSync
(
JSON
.
encode
(
json
),
mode:
FileMode
.
WRITE
,
flush:
true
);
}
packages/flutter_tools/test/android_device_test.dart
View file @
67046f93
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:flutter_tools/src/android/
device_android
.dart'
;
import
'package:flutter_tools/src/android/
android_device
.dart'
;
import
'package:test/test.dart'
;
import
'package:test/test.dart'
;
import
'src/context.dart'
;
import
'src/context.dart'
;
...
...
packages/flutter_tools/test/base_utils_test.dart
0 → 100644
View file @
67046f93
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:flutter_tools/src/base/utils.dart'
;
import
'package:test/test.dart'
;
main
()
=>
defineTests
();
defineTests
()
{
group
(
'ItemListNotifier'
,
()
{
test
(
'sends notifications'
,
()
async
{
ItemListNotifier
<
String
>
list
=
new
ItemListNotifier
<
String
>();
expect
(
list
.
items
,
isEmpty
);
Future
<
List
<
String
>>
addedStreamItems
=
list
.
onAdded
.
toList
();
Future
<
List
<
String
>>
removedStreamItems
=
list
.
onRemoved
.
toList
();
list
.
updateWithNewList
(<
String
>[
'aaa'
]);
list
.
updateWithNewList
(<
String
>[
'aaa'
,
'bbb'
]);
list
.
updateWithNewList
(<
String
>[
'bbb'
]);
list
.
dispose
();
List
<
String
>
addedItems
=
await
addedStreamItems
;
List
<
String
>
removedItems
=
await
removedStreamItems
;
expect
(
addedItems
.
length
,
2
);
expect
(
addedItems
.
first
,
'aaa'
);
expect
(
addedItems
[
1
],
'bbb'
);
expect
(
removedItems
.
length
,
1
);
expect
(
removedItems
.
first
,
'aaa'
);
});
});
}
packages/flutter_tools/test/daemon_test.dart
View file @
67046f93
...
@@ -3,11 +3,14 @@
...
@@ -3,11 +3,14 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:io'
;
import
'package:flutter_tools/src/base/context.dart'
;
import
'package:flutter_tools/src/base/context.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/commands/daemon.dart'
;
import
'package:flutter_tools/src/commands/daemon.dart'
;
import
'package:flutter_tools/src/doctor.dart'
;
import
'package:flutter_tools/src/globals.dart'
;
import
'package:flutter_tools/src/globals.dart'
;
import
'package:flutter_tools/src/ios/mac.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:test/test.dart'
;
import
'package:test/test.dart'
;
...
@@ -31,6 +34,9 @@ defineTests() {
...
@@ -31,6 +34,9 @@ defineTests() {
appContext
=
new
AppContext
();
appContext
=
new
AppContext
();
notifyingLogger
=
new
NotifyingLogger
();
notifyingLogger
=
new
NotifyingLogger
();
appContext
[
Logger
]
=
notifyingLogger
;
appContext
[
Logger
]
=
notifyingLogger
;
appContext
[
Doctor
]
=
new
Doctor
();
if
(
Platform
.
isMacOS
)
appContext
[
XCode
]
=
new
XCode
();
});
});
tearDown
(()
{
tearDown
(()
{
...
...
packages/flutter_tools/test/src/mocks.dart
View file @
67046f93
...
@@ -2,11 +2,12 @@
...
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:flutter_tools/src/android/
device_android
.dart'
;
import
'package:flutter_tools/src/android/
android_device
.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/build_configuration.dart'
;
import
'package:flutter_tools/src/build_configuration.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/ios/device_ios.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/simulators.dart'
;
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
import
'package:flutter_tools/src/runner/flutter_command.dart'
;
import
'package:flutter_tools/src/toolchain.dart'
;
import
'package:flutter_tools/src/toolchain.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:mockito/mockito.dart'
;
...
...
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