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
ef8a8410
Unverified
Commit
ef8a8410
authored
Jan 20, 2022
by
Jenn Magder
Committed by
GitHub
Jan 20, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Take screenshot when drive fails to start app or test (#96828)
parent
f1670fa1
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
256 additions
and
98 deletions
+256
-98
drive.dart
packages/flutter_tools/lib/src/commands/drive.dart
+76
-77
drive_test.dart
...lutter_tools/test/commands.shard/hermetic/drive_test.dart
+180
-21
No files found.
packages/flutter_tools/lib/src/commands/drive.dart
View file @
ef8a8410
...
...
@@ -237,68 +237,74 @@ class DriveCommand extends RunCommandBase {
?
null
:
_fileSystem
.
file
(
stringArg
(
'use-application-binary'
));
if
(
stringArg
(
'use-existing-app'
)
==
null
)
{
await
driverService
.
start
(
buildInfo
,
device
,
debuggingOptions
,
ipv6
,
applicationBinary:
applicationBinary
,
route:
route
,
userIdentifier:
userIdentifier
,
mainPath:
targetFile
,
platformArgs:
<
String
,
Object
>{
if
(
traceStartup
)
'trace-startup'
:
traceStartup
,
if
(
web
)
'--no-launch-chrome'
:
true
,
if
(
boolArg
(
'multidex'
))
'multidex'
:
true
,
try
{
if
(
stringArg
(
'use-existing-app'
)
==
null
)
{
await
driverService
.
start
(
buildInfo
,
device
,
debuggingOptions
,
ipv6
,
applicationBinary:
applicationBinary
,
route:
route
,
userIdentifier:
userIdentifier
,
mainPath:
targetFile
,
platformArgs:
<
String
,
Object
>{
if
(
traceStartup
)
'trace-startup'
:
traceStartup
,
if
(
web
)
'--no-launch-chrome'
:
true
,
if
(
boolArg
(
'multidex'
))
'multidex'
:
true
,
}
);
}
else
{
final
Uri
uri
=
Uri
.
tryParse
(
stringArg
(
'use-existing-app'
));
if
(
uri
==
null
)
{
throwToolExit
(
'Invalid VM Service URI:
${stringArg('use-existing-app')}
'
);
}
);
}
else
{
final
Uri
uri
=
Uri
.
tryParse
(
stringArg
(
'use-existing-app'
));
if
(
uri
==
null
)
{
throwToolExit
(
'Invalid VM Service URI:
${stringArg('use-existing-app')}
'
);
await
driverService
.
reuseApplication
(
uri
,
device
,
debuggingOptions
,
ipv6
,
);
}
await
driverService
.
reuseApplication
(
uri
,
device
,
debuggingOptions
,
ipv6
,
final
int
testResult
=
await
driverService
.
startTest
(
testFile
,
stringsArg
(
'test-arguments'
),
<
String
,
String
>{},
packageConfig
,
chromeBinary:
stringArg
(
'chrome-binary'
),
headless:
boolArg
(
'headless'
),
browserDimension:
stringArg
(
'browser-dimension'
).
split
(
','
),
browserName:
stringArg
(
'browser-name'
),
driverPort:
stringArg
(
'driver-port'
)
!=
null
?
int
.
tryParse
(
stringArg
(
'driver-port'
))
:
null
,
androidEmulator:
boolArg
(
'android-emulator'
),
profileMemory:
stringArg
(
'profile-memory'
),
);
}
final
int
testResult
=
await
driverService
.
startTest
(
testFile
,
stringsArg
(
'test-arguments'
),
<
String
,
String
>{},
packageConfig
,
chromeBinary:
stringArg
(
'chrome-binary'
),
headless:
boolArg
(
'headless'
),
browserDimension:
stringArg
(
'browser-dimension'
).
split
(
','
),
browserName:
stringArg
(
'browser-name'
),
driverPort:
stringArg
(
'driver-port'
)
!=
null
?
int
.
tryParse
(
stringArg
(
'driver-port'
))
:
null
,
androidEmulator:
boolArg
(
'android-emulator'
),
profileMemory:
stringArg
(
'profile-memory'
),
);
if
(
testResult
!=
0
&&
screenshot
!=
null
)
{
await
takeScreenshot
(
device
,
screenshot
,
_fileSystem
,
_logger
,
_fsUtils
)
;
if
(
boolArg
(
'keep-app-running'
)
??
(
argResults
[
'use-existing-app'
]
!=
null
))
{
_logger
.
printStatus
(
'Leaving the application running.'
);
}
else
{
final
File
skslFile
=
stringArg
(
'write-sksl-on-exit'
)
!=
null
?
_fileSystem
.
file
(
stringArg
(
'write-sksl-on-exit'
))
:
null
;
await
driverService
.
stop
(
userIdentifier:
userIdentifier
,
writeSkslOnExit:
skslFile
);
}
if
(
testResult
!=
0
)
{
throwToolExit
(
null
);
}
}
on
Exception
catch
(
_
)
{
// On exceptions, including ToolExit, take a screenshot on the device.
if
(
screenshot
!=
null
)
{
await
_takeScreenshot
(
device
);
}
rethrow
;
}
if
(
boolArg
(
'keep-app-running'
)
??
(
argResults
[
'use-existing-app'
]
!=
null
))
{
_logger
.
printStatus
(
'Leaving the application running.'
);
}
else
{
final
File
skslFile
=
stringArg
(
'write-sksl-on-exit'
)
!=
null
?
_fileSystem
.
file
(
stringArg
(
'write-sksl-on-exit'
))
:
null
;
await
driverService
.
stop
(
userIdentifier:
userIdentifier
,
writeSkslOnExit:
skslFile
);
}
if
(
testResult
!=
0
)
{
throwToolExit
(
null
);
}
return
FlutterCommandResult
.
success
();
}
...
...
@@ -344,27 +350,20 @@ class DriveCommand extends RunCommandBase {
<
String
>[
packageDir
,
'test_driver'
,
...
parts
.
skip
(
1
)]));
return
'
${pathWithNoExtension}
_test
${_fileSystem.path.extension(appFile)}
'
;
}
}
@visibleForTesting
Future
<
void
>
takeScreenshot
(
Device
device
,
String
screenshotPath
,
FileSystem
fileSystem
,
Logger
logger
,
FileSystemUtils
fileSystemUtils
,
)
async
{
try
{
final
Directory
outputDirectory
=
fileSystem
.
directory
(
screenshotPath
);
outputDirectory
.
createSync
(
recursive:
true
);
final
File
outputFile
=
fileSystemUtils
.
getUniqueFile
(
outputDirectory
,
'drive'
,
'png'
,
);
await
device
.
takeScreenshot
(
outputFile
);
logger
.
printStatus
(
'Screenshot written to
${outputFile.path}
'
);
}
on
Exception
catch
(
error
)
{
logger
.
printError
(
'Error taking screenshot:
$error
'
);
Future
<
void
>
_takeScreenshot
(
Device
device
)
async
{
try
{
final
Directory
outputDirectory
=
_fileSystem
.
directory
(
screenshot
)
..
createSync
(
recursive:
true
);
final
File
outputFile
=
_fsUtils
.
getUniqueFile
(
outputDirectory
,
'drive'
,
'png'
,
);
await
device
.
takeScreenshot
(
outputFile
);
_logger
.
printStatus
(
'Screenshot written to
${outputFile.path}
'
);
}
on
Exception
catch
(
error
)
{
_logger
.
printError
(
'Error taking screenshot:
$error
'
);
}
}
}
packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
View file @
ef8a8410
...
...
@@ -5,13 +5,19 @@
// @dart = 2.8
import
'package:file/memory.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/base/common.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/build_info.dart'
;
import
'package:flutter_tools/src/cache.dart'
;
import
'package:flutter_tools/src/commands/drive.dart'
;
import
'package:flutter_tools/src/dart/pub.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/drive/drive_service.dart'
;
import
'package:flutter_tools/src/project.dart'
;
import
'package:package_config/package_config.dart'
;
import
'package:test/fake.dart'
;
import
'../../src/common.dart'
;
...
...
@@ -22,11 +28,13 @@ void main() {
FileSystem
fileSystem
;
BufferLogger
logger
;
Platform
platform
;
FakeDeviceManager
fakeDeviceManager
;
setUp
(()
{
fileSystem
=
MemoryFileSystem
.
test
();
logger
=
BufferLogger
.
test
();
platform
=
FakePlatform
();
fakeDeviceManager
=
FakeDeviceManager
();
});
setUpAll
(()
{
...
...
@@ -37,41 +45,106 @@ void main() {
Cache
.
enableLocking
();
});
testUsingContext
(
'takes screenshot and rethrows on drive exception'
,
()
async
{
final
DriveCommand
command
=
DriveCommand
(
fileSystem:
fileSystem
,
logger:
logger
,
platform:
platform
);
fileSystem
.
file
(
'lib/main.dart'
).
createSync
(
recursive:
true
);
fileSystem
.
file
(
'test_driver/main_test.dart'
).
createSync
(
recursive:
true
);
fileSystem
.
file
(
'pubspec.yaml'
).
createSync
();
fileSystem
.
directory
(
'drive_screenshots'
).
createSync
();
testWithoutContext
(
'drive --screenshot writes to expected output'
,
()
async
{
f
inal
Device
screenshotDevice
=
ScreenshotDevice
()
;
final
Device
screenshotDevice
=
ThrowingScreenshotDevice
();
f
akeDeviceManager
.
devices
=
<
Device
>[
screenshotDevice
]
;
await
takeScreenshot
(
screenshotDevice
,
'drive_screenshots'
,
fileSystem
,
logger
,
FileSystemUtils
(
fileSystem:
fileSystem
,
platform:
platform
,
),
await
expectLater
(()
=>
createTestCommandRunner
(
command
).
run
(
<
String
>[
'drive'
,
'--no-pub'
,
'-d'
,
screenshotDevice
.
id
,
'--screenshot'
,
'drive_screenshots'
,
]),
throwsToolExit
(
message:
'cannot start app'
),
);
expect
(
logger
.
statusText
,
contains
(
'Screenshot written to drive_screenshots/drive_01.png'
));
expect
(
logger
.
statusText
,
isNot
(
contains
(
'drive_02.png'
)));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
Pub:
()
=>
FakePub
(),
DeviceManager:
()
=>
fakeDeviceManager
,
});
testWithoutContext
(
'drive --screenshot errors but does not fail if screenshot fails'
,
()
async
{
testUsingContext
(
'takes screenshot on drive test failure'
,
()
async
{
final
DriveCommand
command
=
DriveCommand
(
fileSystem:
fileSystem
,
logger:
logger
,
platform:
platform
,
flutterDriverFactory:
FailingFakeFlutterDriverFactory
(),
);
fileSystem
.
file
(
'lib/main.dart'
).
createSync
(
recursive:
true
);
fileSystem
.
file
(
'test_driver/main_test.dart'
).
createSync
(
recursive:
true
);
fileSystem
.
file
(
'pubspec.yaml'
).
createSync
();
fileSystem
.
directory
(
'drive_screenshots'
).
createSync
();
final
Device
screenshotDevice
=
ScreenshotDevice
();
fakeDeviceManager
.
devices
=
<
Device
>[
screenshotDevice
];
await
expectLater
(()
=>
createTestCommandRunner
(
command
).
run
(
<
String
>[
'drive'
,
'--no-pub'
,
'-d'
,
screenshotDevice
.
id
,
'--use-existing-app'
,
'http://localhost:8181'
,
'--keep-app-running'
,
'--screenshot'
,
'drive_screenshots'
,
]),
throwsToolExit
(),
);
expect
(
logger
.
statusText
,
contains
(
'Screenshot written to drive_screenshots/drive_01.png'
));
expect
(
logger
.
statusText
,
isNot
(
contains
(
'drive_02.png'
)));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
Pub:
()
=>
FakePub
(),
DeviceManager:
()
=>
fakeDeviceManager
,
});
testUsingContext
(
'drive --screenshot errors but does not fail if screenshot fails'
,
()
async
{
final
DriveCommand
command
=
DriveCommand
(
fileSystem:
fileSystem
,
logger:
logger
,
platform:
platform
);
fileSystem
.
file
(
'lib/main.dart'
).
createSync
(
recursive:
true
);
fileSystem
.
file
(
'test_driver/main_test.dart'
).
createSync
(
recursive:
true
);
fileSystem
.
file
(
'pubspec.yaml'
).
createSync
();
fileSystem
.
file
(
'drive_screenshots'
).
createSync
();
await
takeScreenshot
(
screenshotDevice
,
'drive_screenshots'
,
fileSystem
,
logger
,
FileSystemUtils
(
fileSystem:
fileSystem
,
platform:
platform
,
),
final
Device
screenshotDevice
=
ThrowingScreenshotDevice
();
fakeDeviceManager
.
devices
=
<
Device
>[
screenshotDevice
];
await
expectLater
(()
=>
createTestCommandRunner
(
command
).
run
(
<
String
>[
'drive'
,
'--no-pub'
,
'-d'
,
screenshotDevice
.
id
,
'--screenshot'
,
'drive_screenshots'
,
]),
throwsToolExit
(
message:
'cannot start app'
),
);
expect
(
logger
.
statusText
,
isEmpty
);
expect
(
logger
.
errorText
,
contains
(
'Error taking screenshot: FileSystemException: Not a directory'
));
},
overrides:
<
Type
,
Generator
>{
FileSystem:
()
=>
fileSystem
,
ProcessManager:
()
=>
FakeProcessManager
.
any
(),
Pub:
()
=>
FakePub
(),
DeviceManager:
()
=>
fakeDeviceManager
,
});
testUsingContext
(
'shouldRunPub is true unless user specifies --no-pub'
,
()
async
{
...
...
@@ -102,10 +175,58 @@ void main() {
});
}
// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
class
ThrowingScreenshotDevice
extends
ScreenshotDevice
{
@override
Future
<
LaunchResult
>
startApp
(
ApplicationPackage
package
,
{
String
mainPath
,
String
route
,
DebuggingOptions
debuggingOptions
,
Map
<
String
,
dynamic
>
platformArgs
,
bool
prebuiltApplication
=
false
,
bool
usesTerminalUi
=
true
,
bool
ipv6
=
false
,
String
userIdentifier
,
})
async
{
throwToolExit
(
'cannot start app'
);
}
}
// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
class
ScreenshotDevice
extends
Fake
implements
Device
{
@override
final
String
name
=
'FakeDevice'
;
@override
final
Category
category
=
Category
.
mobile
;
@override
final
String
id
=
'fake_device'
;
@override
Future
<
TargetPlatform
>
get
targetPlatform
async
=>
TargetPlatform
.
android
;
@override
final
bool
supportsScreenshot
=
true
;
@override
Future
<
LaunchResult
>
startApp
(
ApplicationPackage
package
,
{
String
mainPath
,
String
route
,
DebuggingOptions
debuggingOptions
,
Map
<
String
,
dynamic
>
platformArgs
,
bool
prebuiltApplication
=
false
,
bool
usesTerminalUi
=
true
,
bool
ipv6
=
false
,
String
userIdentifier
,
})
async
=>
LaunchResult
.
succeeded
();
@override
Future
<
void
>
takeScreenshot
(
File
outputFile
)
async
{}
}
...
...
@@ -125,3 +246,41 @@ class FakePub extends Fake implements Pub {
bool
printProgress
=
true
,
})
async
{
}
}
class
FakeDeviceManager
extends
Fake
implements
DeviceManager
{
List
<
Device
>
devices
=
<
Device
>[];
@override
String
specifiedDeviceId
;
@override
Future
<
List
<
Device
>>
getDevices
()
async
=>
devices
;
@override
Future
<
List
<
Device
>>
findTargetDevices
(
FlutterProject
flutterProject
,
{
Duration
timeout
})
async
=>
devices
;
}
class
FailingFakeFlutterDriverFactory
extends
Fake
implements
FlutterDriverFactory
{
@override
DriverService
createDriverService
(
bool
web
)
=>
FailingFakeDriverService
();
}
class
FailingFakeDriverService
extends
Fake
implements
DriverService
{
@override
Future
<
void
>
reuseApplication
(
Uri
vmServiceUri
,
Device
device
,
DebuggingOptions
debuggingOptions
,
bool
ipv6
)
async
{
}
@override
Future
<
int
>
startTest
(
String
testFile
,
List
<
String
>
arguments
,
Map
<
String
,
String
>
environment
,
PackageConfig
packageConfig
,
{
bool
headless
,
String
chromeBinary
,
String
browserName
,
bool
androidEmulator
,
int
driverPort
,
List
<
String
>
browserDimension
,
String
profileMemory
,
})
async
=>
1
;
}
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