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
fb94d3fb
Unverified
Commit
fb94d3fb
authored
May 14, 2020
by
Tong Mu
Committed by
GitHub
May 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Animation sheet recorder (#55527)
parent
533cd7a6
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
606 additions
and
0 deletions
+606
-0
animation_sheet_test.dart
packages/flutter/test/animation/animation_sheet_test.dart
+148
-0
flutter_test.dart
packages/flutter_test/lib/flutter_test.dart
+1
-0
animation_sheet.dart
packages/flutter_test/lib/src/animation_sheet.dart
+350
-0
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+23
-0
widget_tester_test.dart
packages/flutter_test/test/widget_tester_test.dart
+84
-0
No files found.
packages/flutter/test/animation/animation_sheet_test.dart
0 → 100644
View file @
fb94d3fb
// Copyright 2014 The Flutter 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:ui'
;
import
'package:flutter/cupertino.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
/*
* Here lies tests for packages/flutter_test/lib/src/animation_sheet.dart
* because [matchesGoldenFile] does not use Skia Gold in its native package.
*/
testWidgets
(
'correctly records frames'
,
(
WidgetTester
tester
)
async
{
final
AnimationSheetBuilder
builder
=
AnimationSheetBuilder
(
frameSize:
_DecuplePixels
.
size
);
await
tester
.
pumpFrames
(
builder
.
record
(
const
_DecuplePixels
(
Duration
(
seconds:
1
)),
),
const
Duration
(
milliseconds:
200
),
const
Duration
(
milliseconds:
100
),
);
await
tester
.
pumpFrames
(
builder
.
record
(
const
_DecuplePixels
(
Duration
(
seconds:
1
)),
recording:
false
,
),
const
Duration
(
milliseconds:
200
),
const
Duration
(
milliseconds:
100
),
);
await
tester
.
pumpFrames
(
builder
.
record
(
const
_DecuplePixels
(
Duration
(
seconds:
1
)),
recording:
true
,
),
const
Duration
(
milliseconds:
400
),
const
Duration
(
milliseconds:
100
),
);
final
Widget
display
=
await
builder
.
display
();
await
tester
.
binding
.
setSurfaceSize
(
builder
.
sheetSize
());
await
tester
.
pumpWidget
(
display
);
await
expectLater
(
find
.
byWidget
(
display
),
matchesGoldenFile
(
'test.animation_sheet_builder.records.png'
));
},
skip:
isBrowser
);
testWidgets
(
'correctly wraps a row'
,
(
WidgetTester
tester
)
async
{
final
AnimationSheetBuilder
builder
=
AnimationSheetBuilder
(
frameSize:
_DecuplePixels
.
size
);
const
Duration
duration
=
Duration
(
seconds:
2
);
await
tester
.
pumpFrames
(
builder
.
record
(
const
_DecuplePixels
(
duration
)),
duration
,
const
Duration
(
milliseconds:
200
),
);
final
Widget
display
=
await
builder
.
display
();
await
tester
.
binding
.
setSurfaceSize
(
builder
.
sheetSize
(
maxWidth:
80
));
await
tester
.
pumpWidget
(
display
);
await
expectLater
(
find
.
byWidget
(
display
),
matchesGoldenFile
(
'test.animation_sheet_builder.wraps.png'
));
},
skip:
isBrowser
);
}
// An animation of a yellow pixel moving from left to right, in a container of
// (10, 1) with a 1-pixel-wide black border.
class
_DecuplePixels
extends
StatefulWidget
{
const
_DecuplePixels
(
this
.
duration
);
static
const
Size
size
=
Size
(
12
,
3
);
final
Duration
duration
;
@override
State
<
StatefulWidget
>
createState
()
=>
_DecuplePixelsState
();
}
class
_DecuplePixelsState
extends
State
<
_DecuplePixels
>
with
SingleTickerProviderStateMixin
{
AnimationController
_controller
;
@override
void
initState
()
{
super
.
initState
();
_controller
=
AnimationController
(
duration:
widget
.
duration
,
vsync:
this
,
);
_controller
.
repeat
();
}
@override
void
dispose
()
{
_controller
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
AnimatedBuilder
(
animation:
_controller
.
view
,
builder:
(
BuildContext
context
,
Widget
child
)
{
return
CustomPaint
(
painter:
_PaintDecuplePixels
(
_controller
.
value
),
);
},
);
}
}
class
_PaintDecuplePixels
extends
CustomPainter
{
_PaintDecuplePixels
(
this
.
value
);
final
double
value
;
@override
bool
shouldRepaint
(
_PaintDecuplePixels
oldDelegate
)
{
return
oldDelegate
.
value
!=
value
;
}
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
canvas
.
save
();
final
Rect
rect
=
RectTween
(
begin:
const
Rect
.
fromLTWH
(
1
,
1
,
1
,
1
),
end:
const
Rect
.
fromLTWH
(
11
,
1
,
1
,
1
),
).
transform
(
value
);
canvas
.
drawRect
(
rect
,
Paint
()..
color
=
Colors
.
yellow
);
final
Paint
black
=
Paint
()..
color
=
Colors
.
black
;
canvas
// Top border
..
drawRect
(
const
Rect
.
fromLTRB
(
0
,
0
,
12
,
1
),
black
)
// Bottom border
..
drawRect
(
const
Rect
.
fromLTRB
(
0
,
2
,
12
,
3
),
black
)
// Left border
..
drawRect
(
const
Rect
.
fromLTRB
(
0
,
0
,
1
,
3
),
black
)
// Right border
..
drawRect
(
const
Rect
.
fromLTRB
(
11
,
0
,
12
,
3
),
black
);
canvas
.
restore
();
}
}
packages/flutter_test/lib/flutter_test.dart
View file @
fb94d3fb
...
@@ -49,6 +49,7 @@ export 'dart:async' show Future;
...
@@ -49,6 +49,7 @@ export 'dart:async' show Future;
export
'src/_goldens_io.dart'
if
(
dart
.
library
.
html
)
'src/_goldens_web.dart'
;
export
'src/_goldens_io.dart'
if
(
dart
.
library
.
html
)
'src/_goldens_web.dart'
;
export
'src/accessibility.dart'
;
export
'src/accessibility.dart'
;
export
'src/all_elements.dart'
;
export
'src/all_elements.dart'
;
export
'src/animation_sheet.dart'
;
export
'src/binding.dart'
;
export
'src/binding.dart'
;
export
'src/controller.dart'
;
export
'src/controller.dart'
;
export
'src/event_simulation.dart'
;
export
'src/event_simulation.dart'
;
...
...
packages/flutter_test/lib/src/animation_sheet.dart
0 → 100644
View file @
fb94d3fb
This diff is collapsed.
Click to expand it.
packages/flutter_test/lib/src/widget_tester.dart
View file @
fb94d3fb
...
@@ -566,6 +566,29 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
...
@@ -566,6 +566,29 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
}).
then
<
int
>((
_
)
=>
count
);
}).
then
<
int
>((
_
)
=>
count
);
}
}
/// Repeatedly pump frames that render the `target` widget with a fixed time
/// `interval` as many as `maxDuration` allows.
///
/// The `maxDuration` argument is required. The `interval` argument defaults to
/// 16.683 milliseconds (59.94 FPS).
Future
<
void
>
pumpFrames
(
Widget
target
,
Duration
maxDuration
,
[
Duration
interval
=
const
Duration
(
milliseconds:
16
,
microseconds:
683
),
])
{
assert
(
maxDuration
!=
null
);
// The interval following the last frame doesn't have to be within the fullDuration.
Duration
elapsed
=
Duration
.
zero
;
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
binding
.
attachRootWidget
(
target
);
binding
.
scheduleFrame
();
while
(
elapsed
<
maxDuration
)
{
await
binding
.
pump
(
interval
);
elapsed
+=
interval
;
}
});
}
/// Runs a [callback] that performs real asynchronous work.
/// Runs a [callback] that performs real asynchronous work.
///
///
/// This is intended for callers who need to call asynchronous methods where
/// This is intended for callers who need to call asynchronous methods where
...
...
packages/flutter_test/test/widget_tester_test.dart
View file @
fb94d3fb
...
@@ -244,7 +244,9 @@ void main() {
...
@@ -244,7 +244,9 @@ void main() {
expect
(
message
,
contains
(
'Actual: _TextFinder:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo", textDirection: ltr)>
\n
'
));
expect
(
message
,
contains
(
'Actual: _TextFinder:<exactly one widget with text "foo" (ignoring offstage widgets): Text("foo", textDirection: ltr)>
\n
'
));
expect
(
message
,
contains
(
'Which: means one was found but none were expected
\n
'
));
expect
(
message
,
contains
(
'Which: means one was found but none were expected
\n
'
));
});
});
});
group
(
'pumping'
,
()
{
testWidgets
(
'pumping'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'pumping'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Text
(
'foo'
,
textDirection:
TextDirection
.
ltr
));
await
tester
.
pumpWidget
(
const
Text
(
'foo'
,
textDirection:
TextDirection
.
ltr
));
int
count
;
int
count
;
...
@@ -278,6 +280,28 @@ void main() {
...
@@ -278,6 +280,28 @@ void main() {
count
=
await
tester
.
pumpAndSettle
(
const
Duration
(
seconds:
1
));
count
=
await
tester
.
pumpAndSettle
(
const
Duration
(
seconds:
1
));
expect
(
count
,
6
);
expect
(
count
,
6
);
});
});
testWidgets
(
'pumpFrames'
,
(
WidgetTester
tester
)
async
{
final
List
<
int
>
logPaints
=
<
int
>[];
int
initial
;
final
Widget
target
=
_AlwaysAnimating
(
onPaint:
()
{
final
int
current
=
SchedulerBinding
.
instance
.
currentFrameTimeStamp
.
inMicroseconds
;
initial
??=
current
;
logPaints
.
add
(
current
-
initial
);
},
);
await
tester
.
pumpFrames
(
target
,
const
Duration
(
milliseconds:
55
));
expect
(
logPaints
,
<
int
>[
0
,
17000
,
34000
,
50000
]);
logPaints
.
clear
();
await
tester
.
pumpFrames
(
target
,
const
Duration
(
milliseconds:
30
),
const
Duration
(
milliseconds:
10
));
expect
(
logPaints
,
<
int
>[
60000
,
70000
,
80000
]);
});
});
});
group
(
'find.byElementPredicate'
,
()
{
group
(
'find.byElementPredicate'
,
()
{
...
@@ -793,3 +817,63 @@ class _SingleTickerTestState extends State<_SingleTickerTest> with SingleTickerP
...
@@ -793,3 +817,63 @@ class _SingleTickerTestState extends State<_SingleTickerTest> with SingleTickerP
return
Container
();
return
Container
();
}
}
}
}
class
_AlwaysAnimating
extends
StatefulWidget
{
const
_AlwaysAnimating
({
this
.
child
,
this
.
onPaint
,
});
final
Widget
child
;
final
VoidCallback
onPaint
;
@override
State
<
StatefulWidget
>
createState
()
=>
_AlwaysAnimatingState
();
}
class
_AlwaysAnimatingState
extends
State
<
_AlwaysAnimating
>
with
SingleTickerProviderStateMixin
{
AnimationController
_controller
;
@override
void
initState
()
{
super
.
initState
();
_controller
=
AnimationController
(
duration:
const
Duration
(
milliseconds:
100
),
vsync:
this
,
);
_controller
.
repeat
();
}
@override
void
dispose
()
{
_controller
.
dispose
();
super
.
dispose
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
AnimatedBuilder
(
animation:
_controller
.
view
,
builder:
(
BuildContext
context
,
Widget
child
)
{
return
CustomPaint
(
painter:
_AlwaysRepaint
(
widget
.
onPaint
),
child:
widget
.
child
,
);
},
);
}
}
class
_AlwaysRepaint
extends
CustomPainter
{
_AlwaysRepaint
(
this
.
onPaint
);
final
VoidCallback
onPaint
;
@override
bool
shouldRepaint
(
CustomPainter
oldDelegate
)
=>
true
;
@override
void
paint
(
Canvas
canvas
,
Size
size
)
{
onPaint
();
}
}
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