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
b6a2efb7
Commit
b6a2efb7
authored
Apr 18, 2019
by
Kate Lovett
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Committing progress. Documentation and testing incomplete/in progress. Code cleanup needed as well.
parent
efe744a3
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
322 additions
and
159 deletions
+322
-159
flutter_goldens.dart
packages/flutter_goldens/lib/flutter_goldens.dart
+22
-29
client.dart
packages/flutter_goldens_client/lib/client.dart
+243
-130
matchers.dart
packages/flutter_test/lib/src/matchers.dart
+57
-0
No files found.
packages/flutter_goldens/lib/flutter_goldens.dart
View file @
b6a2efb7
// Copyright 2018 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
'dart:typed_data'
;
import
'package:file/file.dart'
;
...
...
@@ -12,6 +12,8 @@ import 'package:meta/meta.dart';
import
'package:flutter_goldens_client/client.dart'
;
export
'package:flutter_goldens_client/client.dart'
;
//TODO(katelovett): Tests [flutter_goldens_test.dart] and inline documentation
const
String
_kFlutterRootKey
=
'FLUTTER_ROOT'
;
/// Main method that can be used in a `flutter_test_config.dart` file to set
/// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that
...
...
@@ -25,12 +27,14 @@ Future<void> main(FutureOr<void> testMain()) async {
///
/// Within the https://github.com/flutter/flutter repository, it's important
/// not to check-in binaries in order to keep the size of the repository to a
/// minimum. To satisfy this requirement, this comparator
retrieves the golden
///
files from a sibling repository, `flutter/goldens`
.
/// minimum. To satisfy this requirement, this comparator
uses the
///
[SkiaGoldClient] to upload widgets for golden tests and process results
.
///
/// This comparator will locally clone the `flutter/goldens` repository into
/// the `$FLUTTER_ROOT/bin/cache/pkg/goldens` folder, then perform the comparison against
/// the files therein.
/// This comparator will instantiate the [SkiaGoldClient] and process the
/// results of the test.
class
FlutterGoldenFileComparator
implements
GoldenFileComparator
{
/// Creates a [FlutterGoldenFileComparator] that will resolve golden file
/// URIs relative to the specified [basedir].
...
...
@@ -49,48 +53,36 @@ class FlutterGoldenFileComparator implements GoldenFileComparator {
@visibleForTesting
final
FileSystem
fs
;
/// Instance of the [SkiaGoldClient] for executing tests.
final
SkiaGoldClient
_skiaClient
=
SkiaGoldClient
();
/// Creates a new [FlutterGoldenFileComparator] that mirrors the relative
/// path resolution of the default [goldenFileComparator].
///
/// By the time the future completes, the clone of the `flutter/goldens`
/// repository is guaranteed to be ready use.
///
/// The [goldens] and [defaultComparator] parameters are visible for testing
/// The [defaultComparator] parameter is visible for testing
/// purposes only.
static
Future
<
FlutterGoldenFileComparator
>
fromDefaultComparator
({
GoldensClient
goldens
,
LocalFileComparator
defaultComparator
,
})
async
{
defaultComparator
??=
goldenFileComparator
;
// Prepare the goldens repo.
goldens
??=
GoldensClient
();
await
goldens
.
prepare
();
// Calculate the appropriate basedir for the current test context.
final
FileSystem
fs
=
goldens
.
fs
;
const
FileSystem
fs
=
LocalFileSystem
()
;
final
Directory
testDirectory
=
fs
.
directory
(
defaultComparator
.
basedir
);
final
String
testDirectoryRelativePath
=
fs
.
path
.
relative
(
testDirectory
.
path
,
from:
goldens
.
flutterRoot
.
path
);
return
FlutterGoldenFileComparator
(
goldens
.
repositoryRoot
.
childDirectory
(
testDirectoryRelativePath
).
uri
);
final
Directory
flutterRoot
=
fs
.
directory
(
Platform
.
environment
[
_kFlutterRootKey
]);
final
Directory
goldenRoot
=
flutterRoot
.
childDirectory
(
fs
.
path
.
join
(
'bin'
,
'cache'
,
'pkg'
,
'goldens'
));
final
String
testDirectoryRelativePath
=
fs
.
path
.
relative
(
testDirectory
.
path
,
from:
flutterRoot
.
path
);
return
FlutterGoldenFileComparator
(
goldenRoot
.
childDirectory
(
testDirectoryRelativePath
).
uri
);
}
@override
Future
<
bool
>
compare
(
Uint8List
imageBytes
,
Uri
golden
)
async
{
final
File
goldenFile
=
_getGoldenFile
(
golden
);
if
(!
goldenFile
.
existsSync
())
{
throw
TestFailure
(
'Could not be compared against non-existent file: "
$golden
"'
);
}
final
List
<
int
>
goldenBytes
=
await
goldenFile
.
readAsBytes
();
// TODO(tvolkert): Improve the intelligence of this comparison.
if
(
goldenBytes
.
length
!=
imageBytes
.
length
)
{
return
false
;
}
for
(
int
i
=
0
;
i
<
goldenBytes
.
length
;
i
++)
{
if
(
goldenBytes
[
i
]
!=
imageBytes
[
i
])
{
return
false
;
}
}
return
true
;
bool
authorized
=
await
_skiaClient
.
auth
(
fs
.
directory
(
basedir
));
bool
tested
=
await
_skiaClient
.
imgtest
(
golden
.
path
,
goldenFile
);
return
true
;
// TEMP
//TODO(katelovett): Process results
}
@override
...
...
@@ -103,4 +95,5 @@ class FlutterGoldenFileComparator implements GoldenFileComparator {
File
_getGoldenFile
(
Uri
uri
)
{
return
fs
.
directory
(
basedir
).
childFile
(
fs
.
file
(
uri
).
path
);
}
}
packages/flutter_goldens_client/lib/client.dart
View file @
b6a2efb7
// Copyright 2018 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.
//TODO(katelovett): Change to Skia Gold Client
import
'dart:async'
;
import
'dart:convert'
as
convert
;
import
'dart:io'
as
io
;
import
'package:file/file.dart'
;
...
...
@@ -13,165 +14,116 @@ import 'package:process/process.dart';
// If you are here trying to figure out how to use golden files in the Flutter
// repo itself, consider reading this wiki page:
// https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter
//TODO(katelovett): Tests [flutter_goldens_test.dart] and inline documentation
const
String
_kFlutterRootKey
=
'FLUTTER_ROOT'
;
const
String
_kGoldctlKey
=
'GOLDCTL'
;
const
String
_kServiceAccountKey
=
'GOLD_SERVICE_ACCOUNT'
;
const
String
_kSkiaGoldInstance
=
'SKIA_GOLD_INSTANCE'
;
/// A class that represents a clone of the https://github.com/flutter/goldens
/// repository, nested within the `bin/cache` directory of the caller's Flutter
/// repository.
class
GoldensClient
{
/// Create a handle to a local clone of the goldens repository.
GoldensClient
({
class
SkiaGoldClient
{
SkiaGoldClient
({
this
.
fs
=
const
LocalFileSystem
(),
this
.
platform
=
const
LocalPlatform
(),
this
.
process
=
const
LocalProcessManager
(),
});
/// The file system to use for storing the local clone of the repository.
///
/// This is useful in tests, where a local file system (the default) can
/// be replaced by a memory file system.
final
FileSystem
fs
;
/// A wrapper for the [dart:io.Platform] API.
///
/// This is useful in tests, where the system platform (the default) can
/// be replaced by a mock platform instance.
final
Platform
platform
;
/// A controller for launching subprocesses.
///
/// This is useful in tests, where the real process manager (the default)
/// can be replaced by a mock process manager that doesn't really create
/// subprocesses.
final
ProcessManager
process
;
Directory
_workDirectory
;
RandomAccessFile
_lock
;
//TODO(katelovett): Environment variables swapped out for final CI implementation
String
get
_goldctl
=>
platform
.
environment
[
_kGoldctlKey
];
String
get
_serviceAccount
=>
platform
.
environment
[
_kServiceAccountKey
];
String
get
_skiaGoldInstance
=>
platform
.
environment
[
_kSkiaGoldInstance
];
Directory
get
_flutterRoot
=>
fs
.
directory
(
platform
.
environment
[
_kFlutterRootKey
]);
/// The local [Directory] where the Flutter repository is hosted.
///
/// Uses the [fs] file system.
Directory
get
flutterRoot
=>
fs
.
directory
(
platform
.
environment
[
_kFlutterRootKey
]);
Future
<
bool
>
auth
(
Directory
workDirectory
)
async
{
_workDirectory
=
workDirectory
;
List
<
String
>
authArguments
=
<
String
>[
'auth'
];
if
(
_serviceAccount
==
null
)
throw
const
NonZeroExitCode
(
1
,
'No Service Account found.'
);
/// The local [Directory] where the goldens repository is hosted.
///
/// Uses the [fs] file system.
Directory
get
repositoryRoot
=>
flutterRoot
.
childDirectory
(
fs
.
path
.
join
(
'bin'
,
'cache'
,
'pkg'
,
'goldens'
))
;
authArguments
+=
<
String
>[
'--service-account'
,
_serviceAccount
,
'--work-dir'
,
_workDirectory
.
childDirectory
(
'temp'
).
path
,
]
;
/// Prepares the local clone of the `flutter/goldens` repository for golden
/// file testing.
///
/// This ensures that the goldens repository has been cloned into its
/// expected location within `bin/cache` and that it is synced to the Git
/// revision specified in `bin/internal/goldens.version`.
///
/// While this is preparing the repository, it obtains a file lock such that
/// [GoldensClient] instances in other processes or isolates will not
/// duplicate the work that this is doing.
Future
<
void
>
prepare
()
async
{
final
String
goldensCommit
=
await
_getGoldensCommit
();
String
currentCommit
=
await
_getCurrentCommit
();
if
(
currentCommit
!=
goldensCommit
)
{
await
_obtainLock
();
try
{
// Check the current commit again now that we have the lock.
currentCommit
=
await
_getCurrentCommit
();
if
(
currentCommit
!=
goldensCommit
)
{
if
(
currentCommit
==
null
)
{
await
_initRepository
();
}
await
_checkCanSync
();
await
_syncTo
(
goldensCommit
);
}
}
finally
{
await
_releaseLock
();
}
final
io
.
ProcessResult
authResults
=
io
.
Process
.
runSync
(
_goldctl
,
authArguments
);
if
(
authResults
.
exitCode
!=
0
)
{
final
StringBuffer
buf
=
StringBuffer
();
buf
..
writeln
(
'Flutter + Skia Gold auth failed.'
)
..
writeln
(
'stdout:
${authResults.stdout}
'
)
..
writeln
(
'stderr:
${authResults.stderr}
'
);
throw
NonZeroExitCode
(
authResults
.
exitCode
,
buf
.
toString
());
}
return
true
;
}
Future
<
String
>
_getGoldensCommit
()
async
{
final
File
versionFile
=
flutterRoot
.
childFile
(
fs
.
path
.
join
(
'bin'
,
'internal'
,
'goldens.version'
));
return
(
await
versionFile
.
readAsString
()).
trim
();
}
Future
<
bool
>
imgtest
(
String
testName
,
File
goldenFile
)
async
{
List
<
String
>
imgtestArguments
=
<
String
>[
'imgtest'
,
'add'
,
];
Future
<
String
>
_getCurrentCommit
()
async
{
if
(!
repositoryRoot
.
existsSync
())
{
return
null
;
}
else
{
final
io
.
ProcessResult
revParse
=
await
process
.
run
(
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
],
workingDirectory:
repositoryRoot
.
path
,
);
return
revParse
.
exitCode
==
0
?
revParse
.
stdout
.
trim
()
:
null
;
}
}
final
String
commitHash
=
await
_getCommitHash
();
final
String
keys
=
'
${_workDirectory.path}
keys.json'
;
final
String
failures
=
'
${_workDirectory.path}
failures.json'
;
await
io
.
File
(
keys
).
writeAsString
(
_getKeysJSON
());
await
io
.
File
(
failures
).
create
();
Future
<
void
>
_initRepository
()
async
{
await
repositoryRoot
.
create
(
recursive:
true
);
await
_runCommands
(
<
String
>[
'git init'
,
'git remote add upstream https://github.com/flutter/goldens.git'
,
'git remote set-url --push upstream git@github.com:flutter/goldens.git'
,
],
workingDirectory:
repositoryRoot
,
);
}
imgtestArguments
+=
<
String
>[
'--instance'
,
_skiaGoldInstance
,
'--work-dir'
,
_workDirectory
.
childDirectory
(
'temp'
).
path
,
'--commit'
,
commitHash
,
'--test-name'
,
testName
,
'--png-file'
,
goldenFile
.
path
,
'--keys-file'
,
keys
,
'--failure-file'
,
failures
,
'--passfail'
,
];
Future
<
void
>
_checkCanSync
()
async
{
final
io
.
ProcessResult
result
=
await
process
.
run
(
<
String
>[
'git'
,
'status'
,
'--porcelain'
],
workingDirectory:
repositoryRoot
.
path
,
);
if
(
result
.
stdout
.
trim
().
isNotEmpty
)
{
if
(
imgtestArguments
.
contains
(
null
))
{
final
StringBuffer
buf
=
StringBuffer
();
buf
..
writeln
(
'flutter_goldens git checkout at
${repositoryRoot.path}
has local changes and cannot be synced.'
)
..
writeln
(
'To reset your client to a clean state, and lose any local golden test changes:'
)
..
writeln
(
'cd
${repositoryRoot.path}
'
)
..
writeln
(
'git reset --hard HEAD'
)
..
writeln
(
'git clean -x -d -f -f'
);
buf
.
writeln
(
'null argument for Skia Gold imgtest:'
);
imgtestArguments
.
forEach
(
buf
.
writeln
);
throw
NonZeroExitCode
(
1
,
buf
.
toString
());
}
}
Future
<
void
>
_syncTo
(
String
commit
)
async
{
await
_runCommands
(
<
String
>[
'git pull upstream master'
,
'git fetch upstream
$commit
'
,
'git reset --hard FETCH_HEAD'
,
],
workingDirectory:
repositoryRoot
,
);
final
io
.
ProcessResult
imgtestResult
=
io
.
Process
.
runSync
(
_goldctl
,
imgtestArguments
);
if
(
imgtestResult
.
exitCode
!=
0
)
{
final
StringBuffer
buf
=
StringBuffer
();
buf
..
writeln
(
'Flutter + Skia Gold imgtest failed.'
)
..
writeln
(
'If this is the first execution of this test, it may need to be triaged.'
)
..
writeln
(
'
\t
In this case, re-run the test after triage is completed.'
)
..
writeln
(
'stdout:
${imgtestResult.stdout}
'
)
..
writeln
(
'stderr:
${imgtestResult.stderr}
'
);
throw
NonZeroExitCode
(
imgtestResult
.
exitCode
,
buf
.
toString
());
}
return
true
;
}
Future
<
void
>
_runCommands
(
List
<
String
>
commands
,
{
Directory
workingDirectory
,
})
async
{
for
(
String
command
in
commands
)
{
final
List
<
String
>
parts
=
command
.
split
(
' '
);
final
io
.
ProcessResult
result
=
await
process
.
run
(
parts
,
workingDirectory:
workingDirectory
?.
path
,
Future
<
String
>
_getCommitHash
()
async
{
if
(!
_flutterRoot
.
existsSync
())
{
return
null
;
}
else
{
final
io
.
ProcessResult
revParse
=
await
process
.
run
(
<
String
>[
'git'
,
'rev-parse'
,
'HEAD'
],
workingDirectory:
_flutterRoot
.
path
,
);
if
(
result
.
exitCode
!=
0
)
{
throw
NonZeroExitCode
(
result
.
exitCode
,
result
.
stderr
);
}
return
revParse
.
exitCode
==
0
?
revParse
.
stdout
.
trim
()
:
null
;
}
}
Future
<
void
>
_obtainLock
()
async
{
final
File
lockFile
=
flutterRoot
.
childFile
(
fs
.
path
.
join
(
'bin'
,
'cache'
,
'goldens.lockfile'
));
await
lockFile
.
create
(
recursive:
true
);
_lock
=
await
lockFile
.
open
(
mode:
io
.
FileMode
.
write
);
await
_lock
.
lock
(
io
.
FileLock
.
blockingExclusive
);
}
Future
<
void
>
_releaseLock
()
async
{
await
_lock
.
close
();
_lock
=
null
;
String
_getKeysJSON
()
{
return
convert
.
json
.
encode
(
<
String
,
dynamic
>{
'Operating System'
:
io
.
Platform
.
operatingSystem
,
'Operating System Version'
:
io
.
Platform
.
operatingSystemVersion
,
'Dart Version'
:
io
.
Platform
.
version
,
});
}
}
/// Exception that signals a process' exit with a non-zero exit code.
...
...
@@ -194,3 +146,164 @@ class NonZeroExitCode implements Exception {
return
'Exit code
$exitCode
:
$stderr
'
;
}
}
//
///// A class that represents a clone of the https://github.com/flutter/goldens
///// repository, nested within the `bin/cache` directory of the caller's Flutter
///// repository.
//class GoldensClient {
// /// Create a handle to a local clone of the goldens repository.
// GoldensClient({
// this.fs = const LocalFileSystem(),
// this.platform = const LocalPlatform(),
// this.process = const LocalProcessManager(),
// });
//
// /// The file system to use for storing the local clone of the repository.
// ///
// /// This is useful in tests, where a local file system (the default) can
// /// be replaced by a memory file system.
// final FileSystem fs;
//
// /// A wrapper for the [dart:io.Platform] API.
// ///
// /// This is useful in tests, where the system platform (the default) can
// /// be replaced by a mock platform instance.
// final Platform platform;
//
// /// A controller for launching subprocesses.
// ///
// /// This is useful in tests, where the real process manager (the default)
// /// can be replaced by a mock process manager that doesn't really create
// /// subprocesses.
// final ProcessManager process;
//
// RandomAccessFile _lock;
//
// /// The local [Directory] where the Flutter repository is hosted.
// ///
// /// Uses the [fs] file system.
// Directory get flutterRoot => fs.directory(platform.environment[_kFlutterRootKey]);
//
// /// The local [Directory] where the goldens repository is hosted.
// ///
// /// Uses the [fs] file system.
// Directory get repositoryRoot => flutterRoot.childDirectory(fs.path.join('bin', 'cache', 'pkg', 'goldens'));
//
// /// Prepares the local clone of the `flutter/goldens` repository for golden
// /// file testing.
// ///
// /// This ensures that the goldens repository has been cloned into its
// /// expected location within `bin/cache` and that it is synced to the Git
// /// revision specified in `bin/internal/goldens.version`.
// ///
// /// While this is preparing the repository, it obtains a file lock such that
// /// [GoldensClient] instances in other processes or isolates will not
// /// duplicate the work that this is doing.
// Future<void> prepare() async {
// print('GoldensClient.prepare');
// final String goldensCommit = await _getGoldensCommit();
// String currentCommit = await _getCurrentCommit();
// if (currentCommit != goldensCommit) {
// await _obtainLock();
// try {
// // Check the current commit again now that we have the lock.
// currentCommit = await _getCurrentCommit();
// if (currentCommit != goldensCommit) {
// if (currentCommit == null) {
// await _initRepository();
// }
// await _checkCanSync();
// await _syncTo(goldensCommit);
// }
// } finally {
// await _releaseLock();
// }
// }
// }
//
// Future<String> _getGoldensCommit() async {
// final File versionFile = flutterRoot.childFile(fs.path.join('bin', 'internal', 'goldens.version'));
// return (await versionFile.readAsString()).trim();
// }
//
// Future<String> _getCurrentCommit() async {
// if (!repositoryRoot.existsSync()) {
// return null;
// } else {
// final io.ProcessResult revParse = await process.run(
// <String>['git', 'rev-parse', 'HEAD'],
// workingDirectory: repositoryRoot.path,
// );
// return revParse.exitCode == 0 ? revParse.stdout.trim() : null;
// }
// }
//
// Future<void> _initRepository() async {
// await repositoryRoot.create(recursive: true);
// await _runCommands(
// <String>[
// 'git init',
// 'git remote add upstream https://github.com/flutter/goldens.git',
// 'git remote set-url --push upstream git@github.com:flutter/goldens.git',
// ],
// workingDirectory: repositoryRoot,
// );
// }
//
// Future<void> _checkCanSync() async {
// final io.ProcessResult result = await process.run(
// <String>['git', 'status', '--porcelain'],
// workingDirectory: repositoryRoot.path,
// );
// if (result.stdout.trim().isNotEmpty) {
// final StringBuffer buf = StringBuffer();
// buf
// ..writeln('flutter_goldens git checkout at ${repositoryRoot.path} has local changes and cannot be synced.')
// ..writeln('To reset your client to a clean state, and lose any local golden test changes:')
// ..writeln('cd ${repositoryRoot.path}')
// ..writeln('git reset --hard HEAD')
// ..writeln('git clean -x -d -f -f');
// throw NonZeroExitCode(1, buf.toString());
// }
// }
//
// Future<void> _syncTo(String commit) async {
// await _runCommands(
// <String>[
// 'git pull upstream master',
// 'git fetch upstream $commit',
// 'git reset --hard FETCH_HEAD',
// ],
// workingDirectory: repositoryRoot,
// );
// }
//
// Future<void> _runCommands(
// List<String> commands, {
// Directory workingDirectory,
// }) async {
// for (String command in commands) {
// final List<String> parts = command.split(' ');
// final io.ProcessResult result = await process.run(
// parts,
// workingDirectory: workingDirectory?.path,
// );
// if (result.exitCode != 0) {
// throw NonZeroExitCode(result.exitCode, result.stderr);
// }
// }
// }
//
// Future<void> _obtainLock() async {
// final File lockFile = flutterRoot.childFile(fs.path.join('bin', 'cache', 'goldens.lockfile'));
// await lockFile.create(recursive: true);
// _lock = await lockFile.open(mode: io.FileMode.write);
// await _lock.lock(io.FileLock.blockingExclusive);
// }
//
// Future<void> _releaseLock() async {
// await _lock.close();
// _lock = null;
// }
//}
\ No newline at end of file
packages/flutter_test/lib/src/matchers.dart
View file @
b6a2efb7
...
...
@@ -296,6 +296,16 @@ AsyncMatcher matchesGoldenFile(dynamic key) {
throw
ArgumentError
(
'Unexpected type for golden file:
${key.runtimeType}
'
);
}
/// TODO(katelovett): Documentation
AsyncMatcher
matchesSkiaGoldFile
(
dynamic
key
)
{
if
(
key
is
Uri
)
{
return
_MatchesSkiaGoldFile
(
key
);
}
else
if
(
key
is
String
)
{
return
_MatchesSkiaGoldFile
.
forStringPath
(
key
);
}
throw
ArgumentError
(
'Unexpected type for Skia Gold file:
${key.runtimeType}
'
);
}
/// Asserts that a [Finder], [Future<ui.Image>], or [ui.Image] matches a
/// reference image identified by [image].
///
...
...
@@ -1688,6 +1698,53 @@ class _MatchesGoldenFile extends AsyncMatcher {
description
.
add
(
'one widget whose rasterized image matches golden image "
$key
"'
);
}
class
_MatchesSkiaGoldFile
extends
AsyncMatcher
{
const
_MatchesSkiaGoldFile
(
this
.
key
);
_MatchesSkiaGoldFile
.
forStringPath
(
String
path
)
:
key
=
Uri
.
parse
(
path
);
final
Uri
key
;
@override
Future
<
String
>
matchAsync
(
dynamic
item
)
async
{
Future
<
ui
.
Image
>
imageFuture
;
if
(
item
is
Future
<
ui
.
Image
>)
{
imageFuture
=
item
;
}
else
if
(
item
is
ui
.
Image
)
{
imageFuture
=
Future
<
ui
.
Image
>.
value
(
item
);
}
else
{
final
Finder
finder
=
item
;
final
Iterable
<
Element
>
elements
=
finder
.
evaluate
();
if
(
elements
.
isEmpty
)
{
return
'could not be rendered because no widget was found.'
;
}
else
if
(
elements
.
length
>
1
)
{
return
'matched too many widgets.'
;
}
imageFuture
=
_captureImage
(
elements
.
single
);
}
final
TestWidgetsFlutterBinding
binding
=
TestWidgetsFlutterBinding
.
ensureInitialized
();
return
binding
.
runAsync
<
String
>(()
async
{
final
ui
.
Image
image
=
await
imageFuture
;
final
ByteData
bytes
=
await
image
.
toByteData
(
format:
ui
.
ImageByteFormat
.
png
)
.
timeout
(
const
Duration
(
seconds:
10
),
onTimeout:
()
=>
null
);
if
(
bytes
==
null
)
return
'Failed to generate screenshot from engine within the 10,000ms timeout'
;
await
goldenFileComparator
.
update
(
key
,
bytes
.
buffer
.
asUint8List
());
try
{
final
bool
success
=
await
goldenFileComparator
.
compare
(
null
,
key
);
return
success
?
null
:
'Skia Gold test fail.'
;
}
on
TestFailure
catch
(
ex
)
{
return
ex
.
message
;
}
},
additionalTime:
const
Duration
(
seconds:
11
));
}
@override
Description
describe
(
Description
description
)
=>
description
.
add
(
'one widget whose rasterized images matches Skia Gold image
$key
'
);
}
class
_MatchesSemanticsData
extends
Matcher
{
_MatchesSemanticsData
({
this
.
label
,
...
...
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