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
1acdfb79
Commit
1acdfb79
authored
Feb 24, 2016
by
Yegor
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2120 from yjbanov/driver-wait-on-reads
support waiting for things to happen in Flutter Driver
parents
df52a77f
7f397037
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
281 additions
and
7 deletions
+281
-7
driver.dart
packages/flutter_driver/lib/src/driver.dart
+33
-3
extension.dart
packages/flutter_driver/lib/src/extension.dart
+8
-2
matcher_util.dart
packages/flutter_driver/lib/src/matcher_util.dart
+75
-0
retry.dart
packages/flutter_driver/lib/src/retry.dart
+45
-0
pubspec.yaml
packages/flutter_driver/pubspec.yaml
+3
-2
flutter_driver_test.dart
packages/flutter_driver/test/flutter_driver_test.dart
+53
-0
retry_test.dart
packages/flutter_driver/test/retry_test.dart
+64
-0
No files found.
packages/flutter_driver/lib/src/driver.dart
View file @
1acdfb79
...
...
@@ -4,20 +4,32 @@
import
'dart:async'
;
import
'package:vm_service_client/vm_service_client.dart'
;
import
'package:matcher/matcher.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'error.dart'
;
import
'find.dart'
;
import
'gesture.dart'
;
import
'health.dart'
;
import
'matcher_util.dart'
;
import
'message.dart'
;
import
'retry.dart'
;
final
Logger
_log
=
new
Logger
(
'FlutterDriver'
);
/// Computes a value.
///
/// If computation is asynchronous, the function may return a [Future].
///
/// See also [FlutterDriver.waitFor].
typedef
dynamic
EvaluatorFunction
(
);
/// Drives a Flutter Application running in another process.
class
FlutterDriver
{
static
const
String
_flutterExtensionMethod
=
'ext.flutter_driver'
;
static
const
String
_kFlutterExtensionMethod
=
'ext.flutter_driver'
;
static
const
Duration
_kDefaultTimeout
=
const
Duration
(
seconds:
5
);
static
const
Duration
_kDefaultPauseBetweenRetries
=
const
Duration
(
milliseconds:
160
);
/// Connects to a Flutter application.
///
...
...
@@ -62,7 +74,7 @@ class FlutterDriver {
// Waits for a signal from the VM service that the extension is registered
Future
waitForServiceExtension
()
{
return
isolate
.
onExtensionAdded
.
firstWhere
((
String
extension
)
{
return
extension
==
_
f
lutterExtensionMethod
;
return
extension
==
_
kF
lutterExtensionMethod
;
});
}
...
...
@@ -124,7 +136,7 @@ class FlutterDriver {
Future
<
Map
<
String
,
dynamic
>>
_sendCommand
(
Command
command
)
async
{
Map
<
String
,
dynamic
>
json
=
<
String
,
dynamic
>{
'kind'
:
command
.
kind
}
..
addAll
(
command
.
toJson
());
return
_appIsolate
.
invokeExtension
(
_
f
lutterExtensionMethod
,
json
)
return
_appIsolate
.
invokeExtension
(
_
kF
lutterExtensionMethod
,
json
)
.
then
((
Map
<
String
,
dynamic
>
result
)
=>
result
,
onError:
(
error
,
stackTrace
)
{
throw
new
DriverError
(
'Failed to fulfill
${command.runtimeType}
due to remote error'
,
...
...
@@ -152,6 +164,24 @@ class FlutterDriver {
return
result
.
text
;
}
/// Calls the [evaluator] repeatedly until the result of the evaluation
/// satisfies the [matcher].
///
/// Returns the result of the evaluation.
Future
<
String
>
waitFor
(
EvaluatorFunction
evaluator
,
Matcher
matcher
,
{
Duration
timeout:
_kDefaultTimeout
,
Duration
pauseBetweenRetries:
_kDefaultPauseBetweenRetries
})
async
{
return
retry
(()
async
{
dynamic
value
=
await
evaluator
();
MatchResult
matchResult
=
match
(
value
,
matcher
);
if
(!
matchResult
.
hasMatched
)
{
return
new
Future
.
error
(
matchResult
.
mismatchDescription
);
}
return
value
;
},
timeout
,
pauseBetweenRetries
);
}
/// Closes the underlying connection to the VM service.
///
/// Returns a [Future] that fires once the connection has been closed.
...
...
packages/flutter_driver/lib/src/extension.dart
View file @
1acdfb79
...
...
@@ -13,8 +13,11 @@ import 'find.dart';
import
'gesture.dart'
;
import
'health.dart'
;
import
'message.dart'
;
import
'retry.dart'
;
const
String
_extensionMethod
=
'ext.flutter_driver'
;
const
Duration
_kDefaultTimeout
=
const
Duration
(
seconds:
5
);
const
Duration
_kDefaultPauseBetweenRetries
=
const
Duration
(
milliseconds:
160
);
bool
_flutterDriverExtensionEnabled
=
false
;
...
...
@@ -93,8 +96,11 @@ class FlutterDriverExtension {
Future
<
Health
>
getHealth
(
GetHealth
command
)
async
=>
new
Health
(
HealthStatus
.
ok
);
Future
<
ObjectRef
>
findByValueKey
(
FindByValueKey
command
)
{
Element
elem
=
prober
.
findElementByKey
(
new
ValueKey
<
dynamic
>(
command
.
keyValue
));
Future
<
ObjectRef
>
findByValueKey
(
FindByValueKey
command
)
async
{
Element
elem
=
await
retry
(()
{
return
prober
.
findElementByKey
(
new
ValueKey
<
dynamic
>(
command
.
keyValue
));
},
_kDefaultTimeout
,
_kDefaultPauseBetweenRetries
);
ObjectRef
elemRef
=
elem
!=
null
?
new
ObjectRef
(
_registerObject
(
elem
))
:
new
ObjectRef
.
notFound
();
...
...
packages/flutter_driver/lib/src/matcher_util.dart
0 → 100644
View file @
1acdfb79
// 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
'package:matcher/matcher.dart'
;
/// Matches [value] against the [matcher].
MatchResult
match
(
dynamic
value
,
Matcher
matcher
)
{
if
(
matcher
.
matches
(
value
,
{}))
{
return
new
MatchResult
.
_matched
();
}
else
{
Description
description
=
matcher
.
describeMismatch
(
value
,
new
_TextDescription
(),
{},
false
);
return
new
MatchResult
.
_mismatched
(
description
.
toString
());
}
}
/// Result of matching a value against a matcher.
class
MatchResult
{
MatchResult
.
_matched
()
:
hasMatched
=
true
,
mismatchDescription
=
null
;
MatchResult
.
_mismatched
(
String
mismatchDescription
)
:
hasMatched
=
false
,
mismatchDescription
=
mismatchDescription
;
/// Whether the match succeeded.
final
bool
hasMatched
;
/// If the match did not succeed, this field contains the explanation.
final
String
mismatchDescription
;
}
/// Writes description into a string.
class
_TextDescription
implements
Description
{
final
StringBuffer
_text
=
new
StringBuffer
();
int
get
length
=>
_text
.
length
;
Description
add
(
String
text
)
{
_text
.
write
(
text
);
return
this
;
}
Description
replace
(
String
text
)
{
_text
.
clear
();
_text
.
write
(
text
);
return
this
;
}
Description
addDescriptionOf
(
value
)
{
if
(
value
is
Matcher
)
{
value
.
describe
(
this
);
return
this
;
}
else
{
return
add
(
'
$value
'
);
}
}
Description
addAll
(
String
start
,
String
separator
,
String
end
,
Iterable
list
)
{
add
(
start
);
if
(
list
.
isNotEmpty
)
{
addDescriptionOf
(
list
.
first
);
for
(
dynamic
item
in
list
.
skip
(
1
))
{
add
(
separator
);
addDescriptionOf
(
item
);
}
}
add
(
end
);
return
this
;
}
String
toString
()
=>
'
$_text
'
;
}
packages/flutter_driver/lib/src/retry.dart
0 → 100644
View file @
1acdfb79
// 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'
;
/// Performs an action and returns either the result of the action or a [Future]
/// that evaluates to the result.
typedef
dynamic
Action
(
);
/// Performs [action] repeatedly until it either succeeds or [timeout] limit is
/// reached.
///
/// When the retry time out, the last seen error and stack trace are returned in
/// an error [Future].
Future
<
dynamic
>
retry
(
Action
action
,
Duration
timeout
,
Duration
pauseBetweenRetries
)
async
{
assert
(
action
!=
null
);
assert
(
timeout
!=
null
);
assert
(
pauseBetweenRetries
!=
null
);
Stopwatch
sw
=
new
Stopwatch
()..
start
();
dynamic
result
=
null
;
dynamic
lastError
=
null
;
dynamic
lastStackTrace
=
null
;
bool
success
=
false
;
while
(!
success
&&
sw
.
elapsed
<
timeout
)
{
try
{
result
=
await
action
();
success
=
true
;
}
catch
(
error
,
stackTrace
)
{
lastError
=
error
;
lastStackTrace
=
stackTrace
;
if
(
sw
.
elapsed
<
timeout
)
{
await
new
Future
<
Null
>.
delayed
(
pauseBetweenRetries
);
}
}
}
if
(
success
)
return
result
;
else
return
new
Future
.
error
(
lastError
,
lastStackTrace
);
}
packages/flutter_driver/pubspec.yaml
View file @
1acdfb79
...
...
@@ -8,9 +8,9 @@ environment:
sdk
:
'
>=1.12.0
<2.0.0'
dependencies
:
vm_service_client
:
'
>=0.1.2
<1.0.0'
json_rpc_2
:
any
logging
:
'
>=0.11.0
<1.0.0'
matcher
:
'
>=0.12.0
<1.0.0'
vm_service_client
:
'
>=0.1.2
<1.0.0'
flutter
:
path
:
'
../flutter'
flutter_test
:
...
...
@@ -19,3 +19,4 @@ dependencies:
dev_dependencies
:
test
:
'
>=0.12.6
<1.0.0'
mockito
:
^0.10.1
quiver
:
'
>=0.21.4
<0.22.0'
packages/flutter_driver/test/flutter_driver_test.dart
View file @
1acdfb79
...
...
@@ -10,6 +10,7 @@ import 'package:flutter_driver/src/health.dart';
import
'package:flutter_driver/src/message.dart'
;
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'package:mockito/mockito.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'package:vm_service_client/vm_service_client.dart'
;
main
()
{
...
...
@@ -181,6 +182,58 @@ main() {
expect
(
result
,
'hello'
);
});
});
group
(
'waitFor'
,
()
{
test
(
'waits for a condition'
,
()
{
expect
(
driver
.
waitFor
(()
{
return
new
Future
.
delayed
(
new
Duration
(
milliseconds:
50
),
()
=>
123
);
},
equals
(
123
)),
completion
(
123
)
);
});
test
(
'retries a correct number of times'
,
()
{
new
FakeAsync
().
run
((
FakeAsync
fakeAsync
)
{
int
retryCount
=
0
;
expect
(
driver
.
waitFor
(
()
{
retryCount
++;
return
retryCount
;
},
equals
(
2
),
timeout:
new
Duration
(
milliseconds:
30
),
pauseBetweenRetries:
new
Duration
(
milliseconds:
10
)
),
completion
(
2
)
);
fakeAsync
.
elapse
(
new
Duration
(
milliseconds:
50
));
// Check that we didn't retry more times than necessary
expect
(
retryCount
,
2
);
});
});
test
(
'times out'
,
()
async
{
bool
timedOut
=
false
;
await
driver
.
waitFor
(
()
=>
1
,
equals
(
2
),
timeout:
new
Duration
(
milliseconds:
10
),
pauseBetweenRetries:
new
Duration
(
milliseconds:
2
)
).
catchError
((
err
,
stack
)
{
timedOut
=
true
;
});
expect
(
timedOut
,
isTrue
);
});
});
});
}
...
...
packages/flutter_driver/test/retry_test.dart
0 → 100644
View file @
1acdfb79
// 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
'package:test/test.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'package:flutter_driver/src/retry.dart'
;
main
()
{
group
(
'retry'
,
()
{
test
(
'retries until succeeds'
,
()
{
new
FakeAsync
().
run
((
FakeAsync
fakeAsync
)
{
int
retryCount
=
0
;
expect
(
retry
(
()
async
{
retryCount
++;
if
(
retryCount
<
2
)
{
throw
'error'
;
}
else
{
return
retryCount
;
}
},
new
Duration
(
milliseconds:
30
),
new
Duration
(
milliseconds:
10
)
),
completion
(
2
)
);
fakeAsync
.
elapse
(
new
Duration
(
milliseconds:
50
));
// Check that we didn't retry more times than necessary
expect
(
retryCount
,
2
);
});
});
test
(
'times out returning last error'
,
()
async
{
bool
timedOut
=
false
;
int
retryCount
=
0
;
dynamic
lastError
;
dynamic
lastStackTrace
;
await
retry
(
()
{
retryCount
++;
throw
'error'
;
},
new
Duration
(
milliseconds:
9
),
new
Duration
(
milliseconds:
2
)
).
catchError
((
error
,
stackTrace
)
{
timedOut
=
true
;
lastError
=
error
;
lastStackTrace
=
stackTrace
;
});
expect
(
timedOut
,
isTrue
);
expect
(
lastError
,
'error'
);
expect
(
lastStackTrace
,
isNotNull
);
expect
(
retryCount
,
4
);
});
});
}
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