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
Expand all
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
This diff is collapsed.
Click to expand it.
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