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
fca41bc2
Commit
fca41bc2
authored
Mar 04, 2016
by
Kris Giesing
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add tests for AssetVendor and resolution-dependent image loading
Fixes #2198
parent
7a2d82d0
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
312 additions
and
11 deletions
+312
-11
asset_vendor.dart
packages/flutter/lib/src/widgets/asset_vendor.dart
+16
-4
binding.dart
packages/flutter/lib/src/widgets/binding.dart
+1
-3
asset_vendor_test.dart
packages/flutter/test/widget/asset_vendor_test.dart
+225
-0
instrumentation.dart
packages/flutter_test/lib/src/instrumentation.dart
+2
-1
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+68
-3
No files found.
packages/flutter/lib/src/widgets/asset_vendor.dart
View file @
fca41bc2
...
...
@@ -5,6 +5,7 @@
import
'dart:async'
;
import
'dart:collection'
;
import
'dart:convert'
;
import
'dart:ui'
as
ui
show
Image
;
import
'package:flutter/services.dart'
;
import
'package:mojo/core.dart'
as
core
;
...
...
@@ -39,25 +40,33 @@ class _ResolvingAssetBundle extends CachingAssetBundle {
}
}
/// Abstraction for reading images out of a Mojo data pipe.
///
/// Useful for mocking purposes in unit tests.
typedef
Future
<
ui
.
Image
>
ImageDecoder
(
core
.
MojoDataPipeConsumer
pipe
);
// Asset bundle that understands how specific asset keys represent image scale.
class
_ResolutionAwareAssetBundle
extends
_ResolvingAssetBundle
{
_ResolutionAwareAssetBundle
({
AssetBundle
bundle
,
_ResolutionAwareAssetResolver
resolver
_ResolutionAwareAssetResolver
resolver
,
ImageDecoder
imageDecoder
})
:
super
(
bundle:
bundle
,
resolver:
resolver
);
)
,
_imageDecoder
=
imageDecoder
;
_ResolutionAwareAssetResolver
get
resolver
=>
super
.
resolver
;
final
ImageDecoder
_imageDecoder
;
Future
<
ImageInfo
>
fetchImage
(
String
key
)
async
{
core
.
MojoDataPipeConsumer
pipe
=
await
load
(
key
);
// At this point the key should be in our key cache, and the image
// resource should be in our image cache
double
scale
=
resolver
.
getScale
(
keyCache
[
key
]);
return
new
ImageInfo
(
image:
await
decodeImageFromDataPipe
(
pipe
),
image:
await
_imageDecoder
(
pipe
),
scale:
scale
);
}
...
...
@@ -183,12 +192,14 @@ class AssetVendor extends StatefulComponent {
Key
key
,
this
.
bundle
,
this
.
devicePixelRatio
,
this
.
child
this
.
child
,
this
.
imageDecoder
:
decodeImageFromDataPipe
})
:
super
(
key:
key
);
final
AssetBundle
bundle
;
final
double
devicePixelRatio
;
final
Widget
child
;
final
ImageDecoder
imageDecoder
;
_AssetVendorState
createState
()
=>
new
_AssetVendorState
();
...
...
@@ -207,6 +218,7 @@ class _AssetVendorState extends State<AssetVendor> {
void
_initBundle
()
{
_bundle
=
new
_ResolutionAwareAssetBundle
(
bundle:
config
.
bundle
,
imageDecoder:
config
.
imageDecoder
,
resolver:
new
_ResolutionAwareAssetResolver
(
bundle:
config
.
bundle
,
devicePixelRatio:
config
.
devicePixelRatio
...
...
packages/flutter/lib/src/widgets/binding.dart
View file @
fca41bc2
...
...
@@ -26,14 +26,12 @@ class BindingObserver {
/// This is the glue that binds the framework to the Flutter engine.
class
WidgetFlutterBinding
extends
BindingBase
with
Scheduler
,
Gesturer
,
MojoShell
,
Renderer
{
WidgetFlutterBinding
.
_
();
/// Creates and initializes the WidgetFlutterBinding. This constructor is
/// idempotent; calling it a second time will just return the
/// previously-created instance.
static
WidgetFlutterBinding
ensureInitialized
()
{
if
(
_instance
==
null
)
new
WidgetFlutterBinding
.
_
();
new
WidgetFlutterBinding
();
return
_instance
;
}
...
...
packages/flutter/test/widget/asset_vendor_test.dart
0 → 100644
View file @
fca41bc2
// 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:ui'
as
ui
show
Image
,
hashValues
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:mojo/core.dart'
as
core
;
import
'package:test/test.dart'
;
class
TestImage
extends
ui
.
Image
{
TestImage
(
this
.
scale
);
final
double
scale
;
int
get
width
=>
(
48
*
scale
).
floor
();
int
get
height
=>
(
48
*
scale
).
floor
();
void
dispose
()
{
}
}
class
TestMojoDataPipeConsumer
extends
core
.
MojoDataPipeConsumer
{
TestMojoDataPipeConsumer
(
this
.
scale
)
:
super
(
null
);
final
double
scale
;
}
String
testManifest
=
'''
{
"assets/image.png" : [
"assets/1.5x/image.png",
"assets/2.0x/image.png",
"assets/3.0x/image.png",
"assets/4.0x/image.png"
]
}
'''
;
class
TestAssetBundle
extends
AssetBundle
{
// Image loading logic routes through load(key)
ImageResource
loadImage
(
String
key
)
=>
null
;
Future
<
String
>
loadString
(
String
key
)
{
if
(
key
==
'AssetManifest.json'
)
return
(
new
Completer
<
String
>()..
complete
(
testManifest
)).
future
;
return
null
;
}
Future
<
core
.
MojoDataPipeConsumer
>
load
(
String
key
)
{
core
.
MojoDataPipeConsumer
pipe
=
null
;
switch
(
key
)
{
case
'assets/image.png'
:
pipe
=
new
TestMojoDataPipeConsumer
(
1.0
);
break
;
case
'assets/1.5x/image.png'
:
pipe
=
new
TestMojoDataPipeConsumer
(
1.5
);
break
;
case
'assets/2.0x/image.png'
:
pipe
=
new
TestMojoDataPipeConsumer
(
2.0
);
break
;
case
'assets/3.0x/image.png'
:
pipe
=
new
TestMojoDataPipeConsumer
(
3.0
);
break
;
case
'assets/4.0x/image.png'
:
pipe
=
new
TestMojoDataPipeConsumer
(
4.0
);
break
;
}
return
(
new
Completer
<
core
.
MojoDataPipeConsumer
>()..
complete
(
pipe
)).
future
;
}
String
toString
()
=>
'
$runtimeType
@
$hashCode
()'
;
}
Future
<
ui
.
Image
>
testDecodeImageFromDataPipe
(
core
.
MojoDataPipeConsumer
pipe
)
{
TestMojoDataPipeConsumer
testPipe
=
pipe
as
TestMojoDataPipeConsumer
;
assert
(
testPipe
!=
null
);
ui
.
Image
image
=
new
TestImage
(
testPipe
.
scale
);
return
(
new
Completer
<
ui
.
Image
>()..
complete
(
image
)).
future
;
}
Widget
buildImageAtRatio
(
String
image
,
Key
key
,
double
ratio
,
bool
inferSize
)
{
const
double
windowSize
=
500.0
;
// 500 logical pixels
const
double
imageSize
=
200.0
;
// 200 logical pixels
return
new
MediaQuery
(
data:
new
MediaQueryData
(
size:
const
Size
(
windowSize
,
windowSize
),
devicePixelRatio:
ratio
,
padding:
const
EdgeDims
.
all
(
0.0
)
),
child:
new
AssetVendor
(
bundle:
new
TestAssetBundle
(),
devicePixelRatio:
ratio
,
imageDecoder:
testDecodeImageFromDataPipe
,
child:
new
Center
(
child:
inferSize
?
new
AssetImage
(
key:
key
,
name:
image
)
:
new
AssetImage
(
key:
key
,
name:
image
,
height:
imageSize
,
width:
imageSize
,
fit:
ImageFit
.
fill
)
)
)
);
}
RenderImage
getRenderImage
(
tester
,
Key
key
)
{
return
tester
.
findElementByKey
(
key
).
renderObject
as
RenderImage
;
}
TestImage
getTestImage
(
tester
,
Key
key
)
{
return
getRenderImage
(
tester
,
key
).
image
as
TestImage
;
}
void
pumpTreeToLayout
(
WidgetTester
tester
,
Widget
widget
)
{
Duration
pumpDuration
=
const
Duration
(
milliseconds:
0
);
EnginePhase
pumpPhase
=
EnginePhase
.
layout
;
tester
.
pumpWidget
(
widget
,
pumpDuration
,
pumpPhase
);
}
void
main
(
)
{
String
image
=
'assets/image.png'
;
test
(
'Image for device pixel ratio 1.0'
,
()
{
const
double
ratio
=
1.0
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.0
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.0
);
});
});
test
(
'Image for device pixel ratio 0.5'
,
()
{
const
double
ratio
=
0.5
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.0
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.0
);
});
});
test
(
'Image for device pixel ratio 1.5'
,
()
{
const
double
ratio
=
1.5
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.5
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.5
);
});
});
test
(
'Image for device pixel ratio 1.75'
,
()
{
const
double
ratio
=
1.75
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.5
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
1.5
);
});
});
test
(
'Image for device pixel ratio 2.3'
,
()
{
const
double
ratio
=
2.3
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
2.0
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
2.0
);
});
});
test
(
'Image for device pixel ratio 3.7'
,
()
{
const
double
ratio
=
3.7
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
4.0
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
4.0
);
});
});
test
(
'Image for device pixel ratio 5.1'
,
()
{
const
double
ratio
=
5.1
;
testWidgets
((
WidgetTester
tester
)
{
Key
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
false
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
200.0
,
200.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
4.0
);
key
=
new
GlobalKey
();
pumpTreeToLayout
(
tester
,
buildImageAtRatio
(
image
,
key
,
ratio
,
true
));
expect
(
getRenderImage
(
tester
,
key
).
size
,
const
Size
(
48.0
,
48.0
));
expect
(
getTestImage
(
tester
,
key
).
scale
,
4.0
);
});
});
}
packages/flutter_test/lib/src/instrumentation.dart
View file @
fca41bc2
...
...
@@ -15,7 +15,8 @@ typedef Point SizeToPointFunction(Size size);
/// This class provides hooks for accessing the rendering tree and dispatching
/// fake tap/drag/etc. events.
class
Instrumentation
{
Instrumentation
()
:
binding
=
WidgetFlutterBinding
.
ensureInitialized
();
Instrumentation
({
WidgetFlutterBinding
binding
})
:
this
.
binding
=
binding
??
WidgetFlutterBinding
.
ensureInitialized
();
final
WidgetFlutterBinding
binding
;
...
...
packages/flutter_test/lib/src/widget_tester.dart
View file @
fca41bc2
...
...
@@ -12,15 +12,69 @@ import 'package:flutter/widgets.dart';
import
'instrumentation.dart'
;
/// Enumeration of possible phases to reach in pumpWidget.
enum
EnginePhase
{
layout
,
compositingBits
,
paint
,
composite
,
flushSemantics
,
sendSemanticsTree
}
class
_SteppedWidgetFlutterBinding
extends
WidgetFlutterBinding
{
/// Creates and initializes the binding. This constructor is
/// idempotent; calling it a second time will just return the
/// previously-created instance.
static
WidgetFlutterBinding
ensureInitialized
()
{
if
(
WidgetFlutterBinding
.
instance
==
null
)
new
_SteppedWidgetFlutterBinding
();
return
WidgetFlutterBinding
.
instance
;
}
EnginePhase
phase
=
EnginePhase
.
sendSemanticsTree
;
// Pump the rendering pipeline up to the given phase.
void
beginFrame
()
{
buildDirtyElements
();
_beginFrame
();
Element
.
finalizeTree
();
}
// Cloned from Renderer.beginFrame() but with early-exit semantics.
void
_beginFrame
()
{
assert
(
renderView
!=
null
);
RenderObject
.
flushLayout
();
if
(
phase
==
EnginePhase
.
layout
)
return
;
RenderObject
.
flushCompositingBits
();
if
(
phase
==
EnginePhase
.
compositingBits
)
return
;
RenderObject
.
flushPaint
();
if
(
phase
==
EnginePhase
.
paint
)
return
;
renderView
.
compositeFrame
();
// this sends the bits to the GPU
if
(
phase
==
EnginePhase
.
composite
)
return
;
if
(
SemanticsNode
.
hasListeners
)
{
RenderObject
.
flushSemantics
();
if
(
phase
==
EnginePhase
.
flushSemantics
)
return
;
SemanticsNode
.
sendSemanticsTree
();
}
}
}
/// Helper class for fluter tests providing fake async.
/// Helper class for flut
t
er tests providing fake async.
///
/// This class extends Instrumentation to also abstract away the beginFrame
/// and async/clock access to allow writing tests which depend on the passage
/// of time without actually moving the clock forward.
class
WidgetTester
extends
Instrumentation
{
WidgetTester
.
_
(
FakeAsync
async
)
:
async
=
async
,
:
super
(
binding:
_SteppedWidgetFlutterBinding
.
ensureInitialized
()),
async
=
async
,
clock
=
async
.
getClock
(
new
DateTime
.
utc
(
2015
,
1
,
1
))
{
timeDilation
=
1.0
;
ui
.
window
.
onBeginFrame
=
null
;
...
...
@@ -32,7 +86,18 @@ class WidgetTester extends Instrumentation {
/// Calls [runApp()] with the given widget, then triggers a frame sequent and
/// flushes microtasks, by calling [pump()] with the same duration (if any).
void
pumpWidget
(
Widget
widget
,
[
Duration
duration
])
{
/// The supplied EnginePhase is the final phase reached during the pump pass;
/// if not supplied, the whole pass is executed.
void
pumpWidget
(
Widget
widget
,
[
Duration
duration
,
EnginePhase
phase
])
{
if
(
binding
is
_SteppedWidgetFlutterBinding
)
{
// Some tests call WidgetFlutterBinding.ensureInitialized() manually, so
// we can't actually be sure we have a stepped binding.
_SteppedWidgetFlutterBinding
steppedBinding
=
binding
;
steppedBinding
.
phase
=
phase
??
EnginePhase
.
sendSemanticsTree
;
}
else
{
// Can't step to a given phase in that case
assert
(
phase
==
null
);
}
runApp
(
widget
);
pump
(
duration
);
}
...
...
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