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
7f397037
Commit
7f397037
authored
Feb 24, 2016
by
yjbanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
support waiting for things to happen in Flutter Driver
parent
81df765d
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 @
7f397037
...
...
@@ -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 @
7f397037
...
...
@@ -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 @
7f397037
// 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 @
7f397037
// 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 @
7f397037
...
...
@@ -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 @
7f397037
...
...
@@ -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 @
7f397037
// 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