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
d5b715d7
Unverified
Commit
d5b715d7
authored
Sep 23, 2020
by
Jenn Magder
Committed by
GitHub
Sep 23, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Stream logging from attached debugger on iOS 13+ (#66399)
parent
658e6c8a
Changes
9
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
971 additions
and
257 deletions
+971
-257
flutter_run_test.dart
dev/automated_tests/flutter_run_test/flutter_run_test.dart
+4
-0
flutter_run_test.dart
dev/devicelab/bin/tasks/flutter_run_test.dart
+19
-5
devices.dart
packages/flutter_tools/lib/src/ios/devices.dart
+73
-12
fallback_discovery.dart
packages/flutter_tools/lib/src/ios/fallback_discovery.dart
+17
-17
ios_deploy.dart
packages/flutter_tools/lib/src/ios/ios_deploy.dart
+237
-15
protocol_discovery.dart
packages/flutter_tools/lib/src/protocol_discovery.dart
+7
-4
ios_deploy_test.dart
...flutter_tools/test/general.shard/ios/ios_deploy_test.dart
+247
-42
ios_device_logger_test.dart
..._tools/test/general.shard/ios/ios_device_logger_test.dart
+261
-128
ios_device_start_prebuilt_test.dart
...est/general.shard/ios/ios_device_start_prebuilt_test.dart
+106
-34
No files found.
dev/automated_tests/flutter_run_test/flutter_run_test.dart
View file @
d5b715d7
...
...
@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:io'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
group
(
'example'
,
()
{
test
(
'passed'
,
()
{
print
(
'This is print'
);
stderr
.
writeln
(
'This is writeln'
);
expect
(
true
,
true
);
});
test
(
'failed'
,
()
{
...
...
dev/devicelab/bin/tasks/flutter_run_test.dart
View file @
d5b715d7
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:convert'
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
...
...
@@ -19,9 +20,10 @@ const Pattern passedMessageMatch = '+0: example passed';
const
Pattern
failedMessageMatch
=
'+1: example failed [E]'
;
const
Pattern
skippedMessageMatch
=
'+1 -1: example skipped'
;
const
Pattern
finishedMessageMatch
=
'+1 ~1 -1: Some tests failed.'
;
const
Pattern
printMessageMatch
=
'This is print'
;
const
Pattern
writelnMessageMatch
=
'This is writeln'
;
Future
<
void
>
main
()
async
{
deviceOperatingSystem
=
DeviceOperatingSystem
.
android
;
await
task
(
createFlutterRunTask
);
}
...
...
@@ -31,19 +33,26 @@ Future<TaskResult> createFlutterRunTask() async {
bool
failedTest
=
false
;
bool
skippedTest
=
false
;
bool
finishedMessage
=
false
;
bool
printMessage
=
false
;
bool
writelnMessage
=
false
;
final
Device
device
=
await
devices
.
workingDevice
;
await
device
.
unlock
();
final
List
<
String
>
options
=
<
String
>[
'-t'
,
runTestSource
.
absolute
.
path
,
'-d'
,
device
.
deviceId
,
'-t'
,
runTestSource
.
absolute
.
path
,
'-d'
,
device
.
deviceId
,
'-v'
,
];
await
inDirectory
<
void
>(
flutterGalleryDir
,
()
async
{
startProcess
(
final
Process
run
=
await
startProcess
(
path
.
join
(
flutterDirectory
.
path
,
'bin'
,
'flutter'
),
flutterCommandArgs
(
'run'
,
options
),
environment:
null
,
);
final
Completer
<
void
>
finished
=
Completer
<
void
>();
final
StreamSubscription
<
void
>
subscription
=
device
.
logcat
.
listen
((
String
line
)
{
final
StreamSubscription
<
void
>
subscription
=
run
.
stdout
.
transform
<
String
>(
utf8
.
decoder
)
.
transform
<
String
>(
const
LineSplitter
())
.
listen
((
String
line
)
{
print
(
'stdout:
$line
'
);
// tests execute in order.
if
(
line
.
contains
(
passedMessageMatch
))
{
passedTest
=
true
;
...
...
@@ -51,6 +60,10 @@ Future<TaskResult> createFlutterRunTask() async {
failedTest
=
true
;
}
else
if
(
line
.
contains
(
skippedMessageMatch
))
{
skippedTest
=
true
;
}
else
if
(
line
.
contains
(
printMessageMatch
))
{
printMessage
=
true
;
}
else
if
(
line
.
contains
(
writelnMessageMatch
))
{
writelnMessage
=
true
;
}
else
if
(
line
.
contains
(
finishedMessageMatch
))
{
finishedMessage
=
true
;
finished
.
complete
();
...
...
@@ -58,8 +71,9 @@ Future<TaskResult> createFlutterRunTask() async {
});
await
finished
.
future
.
timeout
(
const
Duration
(
minutes:
1
));
subscription
.
cancel
();
run
.
kill
();
});
return
passedTest
&&
failedTest
&&
skippedTest
&&
finishedMessage
return
passedTest
&&
failedTest
&&
skippedTest
&&
finishedMessage
&&
printMessage
&&
writelnMessage
?
TaskResult
.
success
(<
String
,
dynamic
>{})
:
TaskResult
.
failure
(
'Test did not execute as expected.'
);
}
packages/flutter_tools/lib/src/ios/devices.dart
View file @
d5b715d7
...
...
@@ -213,6 +213,9 @@ class IOSDevice extends Device {
DevicePortForwarder
_portForwarder
;
@visibleForTesting
IOSDeployDebugger
iosDeployDebugger
;
@override
Future
<
bool
>
get
isLocalEmulator
async
=>
false
;
...
...
@@ -396,23 +399,45 @@ class IOSDevice extends Device {
timeout:
timeoutConfiguration
.
slowOperation
);
try
{
ProtocolDiscovery
observatoryDiscovery
;
int
installationResult
=
1
;
if
(
debuggingOptions
.
debuggingEnabled
)
{
_logger
.
printTrace
(
'Debugging is enabled, connecting to observatory'
);
final
DeviceLogReader
deviceLogReader
=
getLogReader
(
app:
package
);
// If the device supports syslog reading, prefer launching the app without
// attaching the debugger to avoid the overhead of the unnecessary extra running process.
if
(
majorSdkVersion
>=
IOSDeviceLogReader
.
minimumUniversalLoggingSdkVersion
)
{
iosDeployDebugger
=
_iosDeploy
.
prepareDebuggerForLaunch
(
deviceId:
id
,
bundlePath:
bundle
.
path
,
launchArguments:
launchArguments
,
interfaceType:
interfaceType
,
);
if
(
deviceLogReader
is
IOSDeviceLogReader
)
{
deviceLogReader
.
debuggerStream
=
iosDeployDebugger
;
}
}
observatoryDiscovery
=
ProtocolDiscovery
.
observatory
(
getLogReader
(
app:
package
)
,
deviceLogReader
,
portForwarder:
portForwarder
,
throttleDuration:
fallbackPollingDelay
,
throttleTimeout:
fallbackThrottleTimeout
??
const
Duration
(
seconds:
5
),
hostPort:
debuggingOptions
.
hostVmServicePort
,
devicePort:
debuggingOptions
.
deviceVmServicePort
,
ipv6:
ipv6
,
throttleTimeout:
fallbackThrottleTimeout
??
const
Duration
(
seconds:
1
),
);
}
final
int
installationResult
=
await
_iosDeploy
.
runApp
(
if
(
iosDeployDebugger
==
null
)
{
installationResult
=
await
_iosDeploy
.
launchApp
(
deviceId:
id
,
bundlePath:
bundle
.
path
,
launchArguments:
launchArguments
,
interfaceType:
interfaceType
,
);
}
else
{
installationResult
=
await
iosDeployDebugger
.
launchAndAttach
()
?
0
:
1
;
}
if
(
installationResult
!=
0
)
{
_logger
.
printError
(
'Could not run
${bundle.path}
on
$id
.'
);
_logger
.
printError
(
'Try launching Xcode and selecting "Product > Run" to fix the problem:'
);
...
...
@@ -466,7 +491,11 @@ class IOSDevice extends Device {
IOSApp
app
,
{
String
userIdentifier
,
})
async
{
// Currently we don't have a way to stop an app running on iOS.
// If the debugger is not attached, killing the ios-deploy process won't stop the app.
if
(
iosDeployDebugger
!=
null
&&
iosDeployDebugger
.
debuggerAttached
)
{
// Avoid null.
return
iosDeployDebugger
?.
exit
()
==
true
;
}
return
false
;
}
...
...
@@ -656,6 +685,13 @@ class IOSDeviceLogReader extends DeviceLogReader {
// Matches a syslog line from any app.
RegExp
_anyLineRegex
;
// Logging from native code/Flutter engine is prefixed by timestamp and process metadata:
// 2020-09-15 19:15:10.931434-0700 Runner[541:226276] Did finish launching.
// 2020-09-15 19:15:10.931434-0700 Runner[541:226276] [Category] Did finish launching.
//
// Logging from the dart code has no prefixing metadata.
final
RegExp
_debuggerLoggingRegex
=
RegExp
(
r'^\S* \S* \S*\[[0-9:]*] (.*)'
);
StreamController
<
String
>
_linesController
;
List
<
StreamSubscription
<
void
>>
_loggingSubscriptions
;
...
...
@@ -672,10 +708,10 @@ class IOSDeviceLogReader extends DeviceLogReader {
_connectedVMService
=
connectedVmService
;
}
static
const
int
_
minimumUniversalLoggingSdkVersion
=
13
;
static
const
int
minimumUniversalLoggingSdkVersion
=
13
;
Future
<
void
>
_listenToUnifiedLoggingEvents
(
vm_service
.
VmService
connectedVmService
)
async
{
if
(
_majorSdkVersion
<
_
minimumUniversalLoggingSdkVersion
)
{
if
(
_majorSdkVersion
<
minimumUniversalLoggingSdkVersion
)
{
return
;
}
try
{
...
...
@@ -692,6 +728,10 @@ class IOSDeviceLogReader extends DeviceLogReader {
}
void
logMessage
(
vm_service
.
Event
event
)
{
if
(
_iosDeployDebugger
!=
null
&&
_iosDeployDebugger
.
debuggerAttached
)
{
// Prefer the more complete logs from the attached debugger.
return
;
}
final
String
message
=
processVmServiceMessage
(
event
);
if
(
message
.
isNotEmpty
)
{
_linesController
.
add
(
message
);
...
...
@@ -704,9 +744,29 @@ class IOSDeviceLogReader extends DeviceLogReader {
]);
}
/// Log reader will listen to [debugger.logLines] and will detach debugger on dispose.
set
debuggerStream
(
IOSDeployDebugger
debugger
)
{
// Logging is gathered from syslog on iOS 13 and earlier.
if
(
_majorSdkVersion
<
minimumUniversalLoggingSdkVersion
)
{
return
;
}
_iosDeployDebugger
=
debugger
;
// Add the debugger logs to the controller created on initialization.
_loggingSubscriptions
.
add
(
debugger
.
logLines
.
listen
(
(
String
line
)
=>
_linesController
.
add
(
_debuggerLineHandler
(
line
)),
onError:
_linesController
.
addError
,
onDone:
_linesController
.
close
,
cancelOnError:
true
,
));
}
IOSDeployDebugger
_iosDeployDebugger
;
// Strip off the logging metadata (leave the category), or just echo the line.
String
_debuggerLineHandler
(
String
line
)
=>
_debuggerLoggingRegex
?.
firstMatch
(
line
)?.
group
(
1
)
??
line
;
void
_listenToSysLog
()
{
// syslog is not written on iOS 13+.
if
(
_majorSdkVersion
>=
_
minimumUniversalLoggingSdkVersion
)
{
if
(
_majorSdkVersion
>=
minimumUniversalLoggingSdkVersion
)
{
return
;
}
_iMobileDevice
.
startLogger
(
_deviceId
).
then
<
void
>((
Process
process
)
{
...
...
@@ -763,6 +823,7 @@ class IOSDeviceLogReader extends DeviceLogReader {
loggingSubscription
.
cancel
();
}
_idevicesyslogProcess
?.
kill
();
_iosDeployDebugger
?.
detach
();
}
}
...
...
packages/flutter_tools/lib/src/ios/fallback_discovery.dart
View file @
d5b715d7
...
...
@@ -82,50 +82,50 @@ class FallbackDiscovery {
}
try
{
final
Uri
result
=
await
_mDnsObservatoryDiscovery
.
getObservatoryUri
(
packageId
,
device
,
usesIpv6:
usesIpv6
,
hostVmservicePort:
hostVmservicePort
,
);
final
Uri
result
=
await
_protocolDiscovery
.
uri
;
if
(
result
!=
null
)
{
UsageEvent
(
_kEventName
,
'
mdns
-success'
,
'
log
-success'
,
flutterUsage:
_flutterUsage
,
).
send
();
return
result
;
}
}
on
ArgumentError
{
// In the event of an invalid InternetAddress, this code attempts to catch
// an ArgumentError from protocol_discovery.dart
}
on
Exception
catch
(
err
)
{
_logger
.
printTrace
(
err
.
toString
());
}
_logger
.
printTrace
(
'Failed to connect with
mDNS, falling back to log scanning
'
);
_logger
.
printTrace
(
'Failed to connect with
log scanning, falling back to mDNS
'
);
UsageEvent
(
_kEventName
,
'
mdns
-failure'
,
'
log
-failure'
,
flutterUsage:
_flutterUsage
,
).
send
();
try
{
final
Uri
result
=
await
_protocolDiscovery
.
uri
;
final
Uri
result
=
await
_mDnsObservatoryDiscovery
.
getObservatoryUri
(
packageId
,
device
,
usesIpv6:
usesIpv6
,
hostVmservicePort:
hostVmservicePort
,
);
if
(
result
!=
null
)
{
UsageEvent
(
_kEventName
,
'
fallback
-success'
,
'
mdns
-success'
,
flutterUsage:
_flutterUsage
,
).
send
();
return
result
;
}
}
on
ArgumentError
{
// In the event of an invalid InternetAddress, this code attempts to catch
// an ArgumentError from protocol_discovery.dart
}
on
Exception
catch
(
err
)
{
_logger
.
printTrace
(
err
.
toString
());
}
_logger
.
printTrace
(
'Failed to connect with
log scanning
'
);
_logger
.
printTrace
(
'Failed to connect with
mDNS
'
);
UsageEvent
(
_kEventName
,
'
fallback
-failure'
,
'
mdns
-failure'
,
flutterUsage:
_flutterUsage
,
).
send
();
return
null
;
...
...
@@ -148,7 +148,7 @@ class FallbackDiscovery {
assumedWsUri
=
Uri
.
parse
(
'ws://localhost:
$hostPort
/ws'
);
}
on
Exception
catch
(
err
)
{
_logger
.
printTrace
(
err
.
toString
());
_logger
.
printTrace
(
'Failed to connect directly, falling back to
mDNS
'
);
_logger
.
printTrace
(
'Failed to connect directly, falling back to
log scanning
'
);
_sendFailureEvent
(
err
,
assumedDevicePort
);
return
null
;
}
...
...
packages/flutter_tools/lib/src/ios/ios_deploy.dart
View file @
d5b715d7
This diff is collapsed.
Click to expand it.
packages/flutter_tools/lib/src/protocol_discovery.dart
View file @
d5b715d7
...
...
@@ -34,7 +34,7 @@ class ProtocolDiscovery {
factory
ProtocolDiscovery
.
observatory
(
DeviceLogReader
logReader
,
{
DevicePortForwarder
portForwarder
,
Duration
throttleDuration
=
const
Duration
(
milliseconds:
200
)
,
Duration
throttleDuration
,
Duration
throttleTimeout
,
@required
int
hostPort
,
@required
int
devicePort
,
...
...
@@ -45,7 +45,7 @@ class ProtocolDiscovery {
logReader
,
kObservatoryService
,
portForwarder:
portForwarder
,
throttleDuration:
throttleDuration
,
throttleDuration:
throttleDuration
??
const
Duration
(
milliseconds:
200
)
,
throttleTimeout:
throttleTimeout
,
hostPort:
hostPort
,
devicePort:
devicePort
,
...
...
@@ -225,7 +225,7 @@ class _BufferedStreamController<T> {
///
/// For example, consider a `waitDuration` of `10ms`, and list of event names
/// and arrival times: `a (0ms), b (5ms), c (11ms), d (21ms)`.
/// The events `
c`
and `d` will be produced as a result.
/// The events `
a`, `c`,
and `d` will be produced as a result.
StreamTransformer
<
S
,
S
>
_throttle
<
S
>({
@required
Duration
waitDuration
,
})
{
...
...
@@ -240,10 +240,13 @@ StreamTransformer<S, S> _throttle<S>({
handleData:
(
S
value
,
EventSink
<
S
>
sink
)
{
latestLine
=
value
;
final
bool
isFirstMessage
=
lastExecution
==
null
;
final
int
currentTime
=
DateTime
.
now
().
millisecondsSinceEpoch
;
lastExecution
??=
currentTime
;
final
int
remainingTime
=
currentTime
-
lastExecution
;
final
int
nextExecutionTime
=
remainingTime
>
waitDuration
.
inMilliseconds
// Always send the first event immediately.
final
int
nextExecutionTime
=
isFirstMessage
||
remainingTime
>
waitDuration
.
inMilliseconds
?
0
:
waitDuration
.
inMilliseconds
-
remainingTime
;
...
...
packages/flutter_tools/test/general.shard/ios/ios_deploy_test.dart
View file @
d5b715d7
...
...
@@ -2,10 +2,15 @@
// 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'
;
import
'package:flutter_tools/src/artifacts.dart'
;
import
'package:flutter_tools/src/base/file_system.dart'
;
import
'package:flutter_tools/src/base/logger.dart'
;
import
'package:flutter_tools/src/base/platform.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/ios_deploy.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:process/process.dart'
;
...
...
@@ -21,7 +26,206 @@ void main () {
expect
(
environment
[
'PATH'
],
startsWith
(
'/usr/bin'
));
});
testWithoutContext
(
'IOSDeploy.uninstallApp calls ios-deploy with correct arguments and returns 0 on success'
,
()
async
{
group
(
'IOSDeploy.prepareDebuggerForLaunch'
,
()
{
testWithoutContext
(
'calls ios-deploy with correct arguments and returns when debugger attaches'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
FakeCommand
(
command:
<
String
>[
'script'
,
'-t'
,
'0'
,
'/dev/null'
,
'ios-deploy'
,
'--id'
,
'123'
,
'--bundle'
,
'/'
,
'--debug'
,
'--args'
,
<
String
>[
'--enable-dart-profiling'
,
].
join
(
' '
),
],
environment:
const
<
String
,
String
>{
'PATH'
:
'/usr/bin:/usr/local/bin:/usr/bin'
,
'DYLD_LIBRARY_PATH'
:
'/path/to/libs'
,
},
stdout:
'(lldb) run
\n
success
\n
Did finish launching.'
,
),
]);
final
IOSDeploy
iosDeploy
=
setUpIOSDeploy
(
processManager
);
final
IOSDeployDebugger
iosDeployDebugger
=
iosDeploy
.
prepareDebuggerForLaunch
(
deviceId:
'123'
,
bundlePath:
'/'
,
launchArguments:
<
String
>[
'--enable-dart-profiling'
],
interfaceType:
IOSDeviceInterface
.
network
,
);
expect
(
await
iosDeployDebugger
.
launchAndAttach
(),
isTrue
);
expect
(
await
iosDeployDebugger
.
logLines
.
toList
(),
<
String
>[
'Did finish launching.'
]);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
});
group
(
'IOSDeployDebugger'
,
()
{
group
(
'launch'
,
()
{
BufferLogger
logger
;
setUp
(()
{
logger
=
BufferLogger
.
test
();
});
testWithoutContext
(
'debugger attached'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
stdout:
'(lldb) run
\r\n
success
\r\n
success
\r\n
Log on attach1
\r\n\r\n
Log on attach2
\r\n\r\n\r\n\r\n
PROCESS_STOPPED
\r\n
Log after process exit'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
final
List
<
String
>
receivedLogLines
=
<
String
>[];
final
Stream
<
String
>
logLines
=
iosDeployDebugger
.
logLines
..
listen
(
receivedLogLines
.
add
);
expect
(
await
iosDeployDebugger
.
launchAndAttach
(),
isTrue
);
await
logLines
.
toList
();
expect
(
receivedLogLines
,
<
String
>[
'success'
,
// ignore first "success" from lldb, but log subsequent ones from real logging.
'Log on attach1'
,
'Log on attach2'
,
''
,
''
]);
});
testWithoutContext
(
'app exit'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
stdout:
'(lldb) run
\r\n
success
\r\n
Log on attach
\r\n
Process 100 exited with status = 0
\r\n
Log after process exit'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
final
List
<
String
>
receivedLogLines
=
<
String
>[];
final
Stream
<
String
>
logLines
=
iosDeployDebugger
.
logLines
..
listen
(
receivedLogLines
.
add
);
expect
(
await
iosDeployDebugger
.
launchAndAttach
(),
isTrue
);
await
logLines
.
toList
();
expect
(
receivedLogLines
,
<
String
>[
'Log on attach'
]);
});
testWithoutContext
(
'attach failed'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
// A success after an error should never happen, but test that we're handling random "successes" anyway.
stdout:
'(lldb) run
\r\n
error: process launch failed
\r\n
success
\r\n
Log on attach1'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
final
List
<
String
>
receivedLogLines
=
<
String
>[];
final
Stream
<
String
>
logLines
=
iosDeployDebugger
.
logLines
..
listen
(
receivedLogLines
.
add
);
expect
(
await
iosDeployDebugger
.
launchAndAttach
(),
isFalse
);
await
logLines
.
toList
();
// Debugger lines are double spaced, separated by an extra \r\n. Skip the extra lines.
// Still include empty lines other than the extra added newlines.
expect
(
receivedLogLines
,
isEmpty
);
});
testWithoutContext
(
'no provisioning profile 1, stdout'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
stdout:
'Error 0xe8008015'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
await
iosDeployDebugger
.
launchAndAttach
();
expect
(
logger
.
errorText
,
contains
(
'No Provisioning Profile was found'
));
});
testWithoutContext
(
'no provisioning profile 2, stderr'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
stderr:
'Error 0xe8000067'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
await
iosDeployDebugger
.
launchAndAttach
();
expect
(
logger
.
errorText
,
contains
(
'No Provisioning Profile was found'
));
});
testWithoutContext
(
'device locked'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
stdout:
'e80000e2'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
await
iosDeployDebugger
.
launchAndAttach
();
expect
(
logger
.
errorText
,
contains
(
'Your device is locked.'
));
});
testWithoutContext
(
'device locked'
,
()
async
{
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
const
FakeCommand
(
command:
<
String
>[
'ios-deploy'
],
stdout:
'Error 0xe8000022'
,
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
logger:
logger
,
);
await
iosDeployDebugger
.
launchAndAttach
();
expect
(
logger
.
errorText
,
contains
(
'Try launching from within Xcode'
));
});
});
testWithoutContext
(
'detach'
,
()
async
{
final
StreamController
<
List
<
int
>>
stdin
=
StreamController
<
List
<
int
>>();
final
Stream
<
String
>
stdinStream
=
stdin
.
stream
.
transform
<
String
>(
const
Utf8Decoder
());
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
FakeCommand
(
command:
const
<
String
>[
'ios-deploy'
,
],
stdout:
'(lldb) run
\n
success'
,
stdin:
IOSink
(
stdin
.
sink
),
),
]);
final
IOSDeployDebugger
iosDeployDebugger
=
IOSDeployDebugger
.
test
(
processManager:
processManager
,
);
await
iosDeployDebugger
.
launchAndAttach
();
iosDeployDebugger
.
detach
();
expect
(
await
stdinStream
.
first
,
'process detach'
);
});
});
group
(
'IOSDeploy.uninstallApp'
,
()
{
testWithoutContext
(
'calls ios-deploy with correct arguments and returns 0 on success'
,
()
async
{
const
String
deviceId
=
'123'
;
const
String
bundleId
=
'com.example.app'
;
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
...
...
@@ -44,7 +248,7 @@ void main () {
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
testWithoutContext
(
'IOSDeploy.uninstallApp
returns non-zero exit code when ios-deploy does the same'
,
()
async
{
testWithoutContext
(
'
returns non-zero exit code when ios-deploy does the same'
,
()
async
{
const
String
deviceId
=
'123'
;
const
String
bundleId
=
'com.example.app'
;
final
FakeProcessManager
processManager
=
FakeProcessManager
.
list
(<
FakeCommand
>[
...
...
@@ -66,6 +270,7 @@ void main () {
expect
(
exitCode
,
1
);
expect
(
processManager
.
hasRemainingExpectations
,
false
);
});
});
}
IOSDeploy
setUpIOSDeploy
(
ProcessManager
processManager
)
{
...
...
packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart
View file @
d5b715d7
...
...
@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/build_info.dart';
import
'package:flutter_tools/src/convert.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/ios_deploy.dart'
;
import
'package:flutter_tools/src/ios/mac.dart'
;
import
'package:mockito/mockito.dart'
;
import
'package:vm_service/vm_service.dart'
;
...
...
@@ -33,6 +34,7 @@ void main() {
.
thenReturn
(
'idevice-syslog'
);
});
group
(
'syslog stream'
,
()
{
testWithoutContext
(
'decodeSyslog decodes a syslog-encoded line'
,
()
{
final
String
decoded
=
decodeSyslog
(
r'I \M-b\M^]\M-$\M-o\M-8\M^O syslog \M-B\M-/\'
...
...
@@ -141,7 +143,9 @@ Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
' with a non-Flutter log message following it.'
,
]);
});
});
group
(
'VM service'
,
()
{
testWithoutContext
(
'IOSDeviceLogReader can listen to VM Service logs'
,
()
async
{
final
MockVmService
vmService
=
MockVmService
();
final
DeviceLogReader
logReader
=
IOSDeviceLogReader
.
test
(
...
...
@@ -191,7 +195,136 @@ Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
]));
verify
(
vmService
.
streamListen
(
'Debug'
));
});
testWithoutContext
(
'IOSDeviceLogReader ignores VM Service logs when attached to debugger'
,
()
async
{
final
MockVmService
vmService
=
MockVmService
();
final
IOSDeviceLogReader
logReader
=
IOSDeviceLogReader
.
test
(
useSyslog:
false
,
iMobileDevice:
IMobileDevice
(
artifacts:
artifacts
,
processManager:
processManager
,
cache:
fakeCache
,
logger:
logger
,
),
);
final
StreamController
<
Event
>
stdoutController
=
StreamController
<
Event
>();
final
StreamController
<
Event
>
stderController
=
StreamController
<
Event
>();
final
Completer
<
Success
>
stdoutCompleter
=
Completer
<
Success
>();
final
Completer
<
Success
>
stderrCompleter
=
Completer
<
Success
>();
when
(
vmService
.
streamListen
(
'Stdout'
)).
thenAnswer
((
Invocation
invocation
)
{
return
stdoutCompleter
.
future
;
});
when
(
vmService
.
streamListen
(
'Stderr'
)).
thenAnswer
((
Invocation
invocation
)
{
return
stderrCompleter
.
future
;
});
when
(
vmService
.
onStdoutEvent
).
thenAnswer
((
Invocation
invocation
)
{
return
stdoutController
.
stream
;
});
when
(
vmService
.
onStderrEvent
).
thenAnswer
((
Invocation
invocation
)
{
return
stderController
.
stream
;
});
logReader
.
connectedVMService
=
vmService
;
stdoutCompleter
.
complete
(
Success
());
stderrCompleter
.
complete
(
Success
());
stdoutController
.
add
(
Event
(
kind:
'Stdout'
,
timestamp:
0
,
bytes:
base64
.
encode
(
utf8
.
encode
(
' This is a message '
)),
));
stderController
.
add
(
Event
(
kind:
'Stderr'
,
timestamp:
0
,
bytes:
base64
.
encode
(
utf8
.
encode
(
' And this is an error '
)),
));
final
MockIOSDeployDebugger
iosDeployDebugger
=
MockIOSDeployDebugger
();
when
(
iosDeployDebugger
.
debuggerAttached
).
thenReturn
(
true
);
final
Stream
<
String
>
debuggingLogs
=
Stream
<
String
>.
fromIterable
(<
String
>[
'Message from debugger'
]);
when
(
iosDeployDebugger
.
logLines
).
thenAnswer
((
Invocation
invocation
)
=>
debuggingLogs
);
logReader
.
debuggerStream
=
iosDeployDebugger
;
// Wait for stream listeners to fire.
await
expectLater
(
logReader
.
logLines
,
emitsInAnyOrder
(<
Matcher
>[
equals
(
'Message from debugger'
),
]));
});
});
group
(
'debugger stream'
,
()
{
testWithoutContext
(
'IOSDeviceLogReader removes metadata prefix from lldb output'
,
()
async
{
final
Stream
<
String
>
debuggingLogs
=
Stream
<
String
>.
fromIterable
(<
String
>[
'2020-09-15 19:15:10.931434-0700 Runner[541:226276] Did finish launching.'
,
'2020-09-15 19:15:10.931434-0700 Runner[541:226276] [Category] Did finish launching from logging category.'
,
'stderr from dart'
,
''
,
]);
final
IOSDeviceLogReader
logReader
=
IOSDeviceLogReader
.
test
(
iMobileDevice:
IMobileDevice
(
artifacts:
artifacts
,
processManager:
processManager
,
cache:
fakeCache
,
logger:
logger
,
),
useSyslog:
false
,
);
final
MockIOSDeployDebugger
iosDeployDebugger
=
MockIOSDeployDebugger
();
when
(
iosDeployDebugger
.
logLines
).
thenAnswer
((
Invocation
invocation
)
=>
debuggingLogs
);
logReader
.
debuggerStream
=
iosDeployDebugger
;
final
Future
<
List
<
String
>>
logLines
=
logReader
.
logLines
.
toList
();
expect
(
await
logLines
,
<
String
>[
'Did finish launching.'
,
'[Category] Did finish launching from logging category.'
,
'stderr from dart'
,
''
,
]);
});
testWithoutContext
(
'errors on debugger stream closes log stream'
,
()
async
{
final
Stream
<
String
>
debuggingLogs
=
Stream
<
String
>.
error
(
'ios-deploy error'
);
final
IOSDeviceLogReader
logReader
=
IOSDeviceLogReader
.
test
(
iMobileDevice:
IMobileDevice
(
artifacts:
artifacts
,
processManager:
processManager
,
cache:
fakeCache
,
logger:
logger
,
),
useSyslog:
false
,
);
final
Completer
<
void
>
streamComplete
=
Completer
<
void
>();
final
MockIOSDeployDebugger
iosDeployDebugger
=
MockIOSDeployDebugger
();
when
(
iosDeployDebugger
.
logLines
).
thenAnswer
((
Invocation
invocation
)
=>
debuggingLogs
);
logReader
.
logLines
.
listen
(
null
,
onError:
(
Object
error
)
=>
streamComplete
.
complete
());
logReader
.
debuggerStream
=
iosDeployDebugger
;
await
streamComplete
.
future
;
});
testWithoutContext
(
'detaches debugger'
,
()
async
{
final
IOSDeviceLogReader
logReader
=
IOSDeviceLogReader
.
test
(
iMobileDevice:
IMobileDevice
(
artifacts:
artifacts
,
processManager:
processManager
,
cache:
fakeCache
,
logger:
logger
,
),
useSyslog:
false
,
);
final
MockIOSDeployDebugger
iosDeployDebugger
=
MockIOSDeployDebugger
();
when
(
iosDeployDebugger
.
logLines
).
thenAnswer
((
Invocation
invocation
)
=>
const
Stream
<
String
>.
empty
());
logReader
.
debuggerStream
=
iosDeployDebugger
;
logReader
.
dispose
();
verify
(
iosDeployDebugger
.
detach
());
});
});
}
class
MockArtifacts
extends
Mock
implements
Artifacts
{}
class
MockVmService
extends
Mock
implements
VmService
{}
class
MockIOSDeployDebugger
extends
Mock
implements
IOSDeployDebugger
{}
packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart
View file @
d5b715d7
This diff is collapsed.
Click to expand it.
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